sample1 = Neurons1 sample2 = Neurons2 sample3 = Glia1 - Astrocytes (CD44+) sample4 = Glia2 - Radial Glia (CD44-)
In HPC I have run steps of scrnabox (custom pipeline in progress) 1. Cell Ranger for feature seq 2. Create Seurat Objects 3. Apply minimum filtering and calculate percent mitochondria.
I have technical 3 replicates with hashtag labels at this point I haven’t yet demultiplex the hashtags. The data here will be treated as one sample. I sorted three separate samples and pooled them together.
0 - NPC or early neurons 1 - immature excitatory neurons 2 - NPC or early neurons 3 - RG or Oligos 4- Dopaminergic neurons - possibly early 5 - NPC or early neurons 6 - Radial GLia
After predicting with the brain data I think using a higher cluster number will work better
Library of tissue cell types for up regulated genes per cluster 0 - hypothalmus, DA A13 1- neural plate, Radial Glia 2 - Neural stem 3 - stromal, astro OPC 4 - Neurons 5 - endothelial, pericyte 6 - maybe neurons maybe not
After predicting with the brain data Bhaduri Midbrain and Striatum - choose main cell types to label Tried the predictions with more clusters 0-9 res 1.2
Next Repeat everything for Neurons2
# explore filtering
seu <- Neurons2
seu
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 2000)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 350)
VlnPlot(seu, pt.size = 0.10, features = c("nCount_RNA"), y.max = 2000)
# filter more cells
seu.ft <- subset(seu, subset = nFeature_RNA > 300 & nCount_RNA > 500 & nCount_RNA < 10000)
seu.ft
# 17604 samples with 250 nFeature_RNA
# 9657 with nFeature 300 and nCOunt 500
Doublet finder
suppressMessages(require(DoubletFinder))
# filtering out MALAT1 and mitochondrial genes
seu.ft <- seu.ft[!grepl("MALAT1", rownames(seu)), ]
seu.ft <- seu.ft[!grepl("^MT-", rownames(seu.ft)), ]
# like in the tutorial I'm following MALAT1 is the top most expressed gene. The top genes are a lot of MT and Ribosomal genes
seu.ft[["percent.rb"]] <- PercentageFeatureSet(seu.ft, pattern = "^RP")
seu.d = NormalizeData(seu.ft)
seu.d = FindVariableFeatures(seu.d, verbose = F)
seu.d = ScaleData(seu.d, vars.to.regress = c("nFeature_RNA", "percent.mt"),
verbose = F)
seu.d = RunPCA(seu.d, verbose = F, npcs = 30)
seu.d = RunUMAP(seu.d, dims = 1:10, verbose = F)
nExp <- round(ncol(seu.d) * 0.08) # expect more doublets because there is a lot more cells
seu.d <- doubletFinder_v3(seu.d, pN = 0.25, pK = 0.09, nExp = nExp, PCs = 1:10)
# name of the DF prediction can change, so extract the correct column name.
DF.name = colnames(seu.d@meta.data)[grepl("DF.classification", colnames(seu.d@meta.data))]
cowplot::plot_grid(ncol = 2, DimPlot(seu.d, group.by = "orig.ident") + NoAxes(),
DimPlot(seu.d, group.by = DF.name) + NoAxes())
VlnPlot(seu.d, features = "nFeature_RNA", group.by = DF.name, pt.size = 0.1)
Remove the doublet cells
seu.d <- seu.d[, seu.d@meta.data[, DF.name]== "Singlet"]
dim(seu.d)
dim(seu)
# 9657 cells pre filter
# 8884 cells after filtering
# note the percent doubles expected is close to the percent detected
Repeat workflow with doublet removed data and find clusters for
seu <- NormalizeData(seu.d, normalization.method = "LogNormalize", scale.factor = 10000)
seu <- FindVariableFeatures(seu, selection.method = "vst", nfeatures = 2000)
seu <- ScaleData(seu)
seu <- RunPCA(seu)
seu <- RunUMAP(seu, reduction = "pca", n.neighbors = 43, dims = 1:30)
DimPlot(seu, reduction = "umap")
seu.q <- FindNeighbors(seu, dims = 1:25, k.param = 43)
seu.q <- FindClusters(seu.q, resolution = c(0,0.2,0.4,0.6))
library(clustree)
clustree(seu.q)
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.2')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.4')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.6')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.1.2')
Label cell types using the label transfer
# SNCA and control midbrain organoids 165 days in culture
MBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AST23_BrainComm/MBOclusters_names29072021.rds")
# Midbrain AIW002 120 days in culture
AIWMBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio120days/MOintegratedClusterK123res0.8.names_nov16_2021")
# Midbrain AIW002 60 days in culture
AIW60 <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio60days/AWI002ParkinKOPinkKO60days_labels_14052022.rds")
# query
#seu.q <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/NeuronsFilteredSeu28092022.RDS")
#first predict with the MBO data
Idents(MBO) <- "cluster_labels"
DefaultAssay(MBO) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = MBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = MBO$cluster_labels)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAST23.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAST23.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id))
pr.t.lables <- as.data.frame(prop.table(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id)))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# clusters don't break up by the predicted cell types
############ another predictions now using the AIW organoids
Idents(AIWMBO) <- "res08names"
DefaultAssay(AIWMBO) <- "RNA"
anchors <- FindTransferAnchors(reference = AIWMBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIWMBO$res08names)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAIW.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAIW.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id))
pr.t.lables <- as.data.frame(prop.table(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id)))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# the predicted cell types make more sense from the AIW002 organoid
# now predict with the AIW002 60 days organoid
Idents(AIW60) <- "cluster.ids"
DefaultAssay(AIW60) <- "RNA"
anchors <- FindTransferAnchors(reference = AIW60, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIW60$cluster.ids)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$AIW60.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW60.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id))
pr.t.lables <- as.data.frame(prop.table(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id)))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# save ojbect with predicitons
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Neurons2PredictionsSeu30092022.RDS")
Predict from Brain Bhahani
seu.q <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Neurons2LabelsSeu30092022.RDS")
# read in the reference dataset
# from Bhaduri midbrain and striatum
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_midbrain_striatum.RDS")
table(seu.r$cell_type)
astrocyte dividing endothelial interneuron microglia neuron
310 351 321 4 30 1596
oligodendrocyte radial glia
195 162
table(seu.r$cell_class)
dividing endothelial glia microglia neuron
351 321 667 30 1600
table(seu.r$cell_cluster)
Astrocyte_4 Astrocyte_5 Dividing_11 Dividing_2 Dividing_3
211 99 10 71 103
Dividing_4 Dividing_42 Dividing_5 Endo_11 Endo_13
38 1 128 28 1
Endo_15 Endo_4 Endo_5 Endo_7 Endo_8
10 144 14 93 31
GW14_Microglia_27 Interneuron_1 Interneuron_24 Interneuron_9 Microglia_10
4 1 2 1 9
Microglia_2 Microglia_8 Neuron_100 Neuron_2 Neuron_22
9 8 1 57 44
Neuron_37 Neuron_4 Neuron_5 Neuron_98 Oligo_11
104 249 1049 92 35
Oligo_2 Oligo_6 Oligo_8 RG_1 RG_37
58 83 19 80 1
RG_4 RG_45 RG_6 RG_7
22 33 25 1
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 1051 anchors
Filtering anchors
Retained 318 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Astrocyte_4 Dividing_3 Dividing_5 Endo_4 Endo_7 Endo_8 Neuron_2 Neuron_22
2105 24 20 987 346 4 32 1
Neuron_37 Neuron_4 Neuron_98 Oligo_11 Oligo_2 RG_1 RG_45 RG_6
3353 1070 866 2 5 6 1 62
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.stri.pred <- Idents(seu.q)
print(table(seu.q$Bha.mid.stri.pred))
Neuron_4 Neuron_37 Astrocyte_4 Neuron_98 Endo_4 Endo_7 RG_6 RG_1
1070 3353 2105 866 987 346 62 6
Dividing_3 Endo_8 Oligo_11 Neuron_2 Dividing_5 Oligo_2 Neuron_22 RG_45
24 4 2 32 20 5 1 1
DimPlot(seu.q, group.by = 'Bha.mid.stri.pred')

DimPlot(seu.q, group.by = 'subgroups')

# do the predictions differ with the main cell type groups instead of the cluster in the reference data?
Idents(seu.r) <- "cell_type"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 1051 anchors
Filtering anchors
Retained 318 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_type)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
astrocyte dividing endothelial neuron oligodendrocyte radial glia
1294 34 1386 5606 53 511
DimPlot(seu.q, group.by = 'predicted.id')

# good largest prediction is neurons.
See the top predictions for each cluster in Neurons2 res 06
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
What cell types are predicted across the 3 references
0 - Neurons early , NPC, neurons excitatory 1 - Neurons early, NPC 2 - Neurons early, NPC, neurons excitatory some DA neurons 3 - Oligo, RG, 4 - Excitatory neurons, NPC, early neurons 5 - DA neurons, early DA neurons 6 - neurons immature NPC 7 - DA neurons 8 - RG, oligo, OPC, NPC 9 - Radial Glia 10 - NPC, neurons, oligo 11 - NPC, neurons, oligo
Find cluster markers and see how those would annotate
Idents(seu.q) <- 'RNA_snn_res.0.6'
ClusterMarkers <- FindAllMarkers(seu.q, only.pos = TRUE)
top5 <- ClusterMarkers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = top5$gene, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
write.csv(ClusterMarkers,"/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/Neurons1ClusterMarkers11.csv")
Cluster 0 has fewer markers. 2 and 5 have similar up reg markers 3 and 4 also overlap
Look at the cluster markers in cell type libraries for Neurons 2
library(enrichR)
db <- c('Allen_Brain_Atlas_up','Descartes_Cell_Types_and_Tissue_2021',
'CellMarker_Augmented_2021','Azimuth_Cell_Types_2021')
# enrichr(genes, databases = NULL)
# cluster 0
N1.c0 <- ClusterMarkers %>% filter(cluster == 0 & avg_log2FC > 0)
genes <- N1.c0$gene
N1.c0.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c0.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c0.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c0.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c0.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
# cluster 1
N1.c1 <- ClusterMarkers %>% filter(cluster == 1 & avg_log2FC > 0)
genes <- N1.c1$gene
N1.c1.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c1.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c1.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c1.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c1.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c1.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c1.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c1.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c1.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 1; olfactory bulb, neural plate, maybe Radial Glia,
N1.c2 <- ClusterMarkers %>% filter(cluster == 2 & avg_log2FC > 0)
genes <- N1.c2$gene
N1.c2.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c2.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c2.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c2.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c2.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c2.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c2.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c2.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c2.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 3
N1.c3 <- ClusterMarkers %>% filter(cluster == 3 & avg_log2FC > 0)
genes <- N1.c3$gene
N1.c3.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c3.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c3.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c3.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c3.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c3.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c3.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c3.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c3.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 4
N1.c4 <- ClusterMarkers %>% filter(cluster == 4 & avg_log2FC > 0)
genes <- N1.c4$gene
N1.c4.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c4.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c4.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c4.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c4.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c4.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c4.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c4.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c4.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 5
N1.c5 <- ClusterMarkers %>% filter(cluster == 5 & avg_log2FC > 0)
genes <- N1.c5$gene
N1.c5.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c5.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c5.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c5.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c5.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c5.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c5.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c5.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c5.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 6
N1.c6 <- ClusterMarkers %>% filter(cluster == 6 & avg_log2FC > 0)
genes <- N1.c6$gene
N1.c6.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c6.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c6.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c6.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c6.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c6.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# other clusters - change the cluster number
N1.c6 <- ClusterMarkers %>% filter(cluster == 11 & avg_log2FC > 0)
genes <- N1.c6$gene
N1.c6.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c6.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c6.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c6.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c6.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c6.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
cluster 0 - astrocyte, radial glia, microglia, striatum - very few genes in these terms cluster 1 - adrenal, thalmus, endothelial, astrocytes, neurons clusters 2 - neural plate, stratum, neurons, NPC, stem, astro,neuroendocrine cluster 3 - brain molecular layer, endothelial, astrocyte embryonic, cluster 4 - DG, striatum CA3, GABAergic neurons cluster 5 - DG, neurons, Glutamatergic neurons cluster 6 - neurons, astrocyte, microglia, GABAneurons cluster 7 - neurons, glut and gaba cluster 8 - astrocyte, endothelial, pericyte cluster 9 - epithelial, embryonic astrocytes, GABA neurons cluster 10 - epithelial cluster 11 - endothelial, immune cells T cells
Expression of markers genes in Neurons2
feature_list = c("MKI67","SOX2","POU5F1","DLX2","PAX6","SOX9","HES1","NES","RBFOX3","MAP2","NCAM1","CD24","GRIA2","GRIN2B","GABBR1","GAD1","GAD2","GABRA1","GABRB2","TH","ALDH1A1","LMX1B","NR4A2","CORIN","CALB1","KCNJ6","CXCR4","ITGA6","SLC1A3","CD44","AQP4","S100B", "PDGFRA","OLIG2","MBP","CLDN11","VIM","VCAM1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = feature_list) +RotatedAxis()
PD_poulin = c("TH","SLC6A3","SLC18A2","SOX6","NDNF","SNCG","ALDH1A1","CALB1","TACR2","SLC17A6","SLC32A1","OTX2","GRP","LPL","CCK","VIP")
DoHeatmap(seu.q, features = PD_poulin, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = PD_poulin)+RotatedAxis()
ealryNeur = c("DCX","NEUROD1","TBR1")
proliferation = c("PCNA","MKI67")
neuralstem = c("SOX2","NES","PAX6","MASH1")
feature_list <- c("DCX","NEUROD1","TBR1","PCNA","MKI67","SOX2","NES","PAX6","MASH1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = feature_list)+RotatedAxis()
# no proliferation marker expression PCNA or MKI67
# cluster 4 DA neurons - shows early neuron marker and low PAX 4
# cluster 3 has higher SOX2 - neuroblast marker / NPC marker
mat_neuron = c("RBFOX3","SYP","DLG45","VAMP1","VAMP2","TUBB3","SYT1","BSN","HOMER1","SLC17A6")
# NeuN is FOX3 - RBFOX3
# PSD95 also SP-90 or DLG4
# VGLUT2 is SLC17A6
DoHeatmap(seu.q, features = mat_neuron, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
# cluster 4 also show mature neuron markers
DotPlot(seu.q, features = mat_neuron)+RotatedAxis()
# excitatory neuron markers
ex = c("GRIA2","GRIA1","GRIA4","GRIN1","GRIN2B","GRIN2A","GRIN3A","GRIN3","GRIP1","CAMK2A")
DoHeatmap(seu.q, features = ex, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = ex)+RotatedAxis()
# inhibitory neuron markers
inh = c("GAD1","GAD2", "GAT1","PVALB","GABR2","GABR1","GBRR1","GABRB2","GABRB1","GABRB3","GABRA6","GABRA1","GABRA4","TRAK2")
DoHeatmap(seu.q, features = inh, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = inh)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
### glia markers
microglia = c("PTPRC","AIF1","ADGRE1") # ADGRE1 is a microglia marker F4/80, CD45 is PTPRC, gene name IBA1 is AIF1
astolgNPCpromicro = c("GFAP","S100B","SLC1A2","MBP","SOX10","SPP1","DCX","NEUROD1","TBR1","PCNA","MKI67","PTPRC","AIF1","ADGRE1")
# note GLT1 is EAAT2 which is SLC1A2 glutatmate transporter
# epithelial
epi = c("HES1","HES5","SOX2","SOX10","NES","CDH1","NOTCH1") # e-cadherin is CDH1
DoHeatmap(seu.q, features = astolgNPCpromicro, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = astolgNPCpromicro, group.by = 'RNA_snn_res.0.6')+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
DoHeatmap(seu.q, features = epi, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = epi, group.by = 'RNA_snn_res.0.6')+RotatedAxis()
# also add Radial glia marker overlap with Glia and Neurons
features <- c("PTPRC","AIF1","ADGRE1", "VIM", "TNC","PTPRZ1","FAM107A","HOPX","LIFR",
"ITGB5","IL6ST")
DoHeatmap(seu.q, features = features, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = features, group.by = 'RNA_snn_res.0.6')+RotatedAxis()
Label the Neuron2 FACS population
Idents(seu.q) <- 'RNA_snn_res.0.6'
cluster.ids <- c("Neurons1","immatureNeurons1","Neurons2",
"Other","DAneurons1","DAneurons2","immatureNeurons2",
"DAneurons3","RG","immatureNeurons1","Epithelial","Endothelial")
unique(seu.q$RNA_snn_res.0.6)
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$subgroups <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'subgroups', repel = TRUE)
# label again with just numbering
# then I'll find markers in pairs to distinguish groups.
Idents(seu.q) <- 'RNA_snn_res.0.6'
cluster.ids <- c("Neurons1","Neurons2","Neurons3",
"Other","DAneurons1","DAneurons2","Neurons4",
"DAneurons3","RG","Neurons2","Epithelial","Endothelial")
unique(seu.q$RNA_snn_res.0.6)
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$number.groups <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'number.groups', repel = TRUE)
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Neurons2LabelsSeu30092022.RDS")
Find markers in pairs to go back and classify the subgroups. Will need to return to this for Neurons1 FACS
Neurons 5 and Neurons2 had similar markers and were merged Subset again
# faster to make a subset objects of only neurons and use find all markers
neuron.sub <- subset(seu.q, idents = c("Neurons1","Neurons2","Neurons3",
"Neurons4"))
neuron.sub.markers <- FindAllMarkers(neuron.sub)
top5 <- neuron.sub.markers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(neuron.sub, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
DotPlot(neuron.sub, features = top5$gene) + RotatedAxis()
# neurons 5 was joined to Neurons 2
# cluster markers done again
# Neurons1 : MGP (targeting neural projections BMP signaling), HPD (excitatory and inibitory, could be cell adhesion or apoptosis), MSX1 (transcription factor BMP signaling, midbrain marker, developmental), CYP1B1 (redox homeostatis)
# MGP, HPD, MSX1, CYP1B1
# there isn't a good proportion of cells expression any of the markers, Neurons2 has the best amount
# Neurons2: TFP12 serine protease melatonin conversion, PTN (cytokine signaling), LYgH (inhances nAChRs), S100A10 (modulates serotonin receptor), IFI27 (antiviral activity)
#TFP12,PTN, LY6H, S100A10, IFI27
# Neurons3: SOX4, ASCL1 (neurogenesis),
# SOX4 is a marker of 3 but has high expression in 4 as well
# Neurons4: PCAT4 (not noted in neurons), TPH1 (5HT synthesis), GK5 (neuronal maintainance), SST (somatostatin, GABA spike regulation), TTR (neural protective in AD)
# PCAT4, TPH1, GK5, SST, TTR
# markers to use
# Neurons1: MSX1, CYP1B1
# Neurons2: LY6H, S100A10
# Neurons3: SOX4, ASCL1 (Neurogenesis) Immature
# Neurons4: GK5, SST More mature
# Maybe neurons 1 and 2 could be merged
# lets see how the markers would look
neurons.1and2 <- FindMarkers(neuron.sub, ident.1 = c("Neurons1","Neurons2"),
ident.2 = c("Neurons3","Neurons4"))
top10 <- neurons.1and2 %>% top_n(n=10, wt = avg_log2FC)
ft.up <- rownames(top10) # up in Neurons1 and 3
top10 <- neurons.1and2 %>% top_n(n=-10, wt = avg_log2FC)
ft.down <- rownames(top10)
features <- c(ft.up,ft.down)
DoHeatmap(neuron.sub, features = features, size=3, angle =90, group.bar.height = 0.02)
DotPlot(neuron.sub, features = features) + RotatedAxis()
# all the markers were up regulated in neurons2 and not really neurons1
# I'll keep them separated
Use subgrouping and find cluster markers to look at neuronal subtypes.
# faster to make a subset objects of only neurons and use find all markers
neuron.sub <- subset(seu.q, idents = c("DAneurons1","DAneurons2","DAneurons3"))
neuron.sub.markers <- FindAllMarkers(neuron.sub)
top5 <- neuron.sub.markers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(neuron.sub, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
DotPlot(neuron.sub, features = top5$gene) + RotatedAxis()
# markers are much clearer for the DA neuron subgroups
# DA neurons 1: WIF1, CYP1B1, IGFBP3, HPD, WFIKKN2
# WIF1 (secreted WNT inhibitor, promotes regeneration), CYP1B1 (redox homeostatis), IGFBP3 (prolactin secretion regulation hypothalmus), HPD (neuro protective), WFIKKN2 (Receptor for TNC)
# DA neurons2: CDH7, RUNX1T1, ASCL1, DLK1, MEG3
# CDH7 (neuro circuitry development, SEMA), RUNX1T1 (neuronal differentiation), ASCL1 (neuronal differentiation), DLK1 (neural differentation), MEG3 (neural homeostatis)
# DA neurons3: PCAT4, NEUROD1, NCKAP5, GK5, SST
# PCAT4 (dendritic growth), NEUROD1 (neural differentation), NCKAP5 (Excitory neurons), GK5 (TH ), SST (regualates spike times)
# DA neurons1: CYP1B1, IGFBP3
# DA neurons2: RUNX1T1, ASCL1
# DA neurons3: NEUROD1, NCKAP5
Name the Neurons2 FACS population with the Neuron subtype latter.
Idents(seu.q) <- 'RNA_snn_res.0.6'
cluster.ids <- c("Neurons-MSX1","Neurons-LY6H","Neurons-ASCL1",
"Other","DAneurons-CYP1B1","DAneurons-ASCL1","Neurons-GK5",
"DAneurons-NEUROD1","RG","Neurons-LY6H","Epithelial","Endothelial")
unique(seu.q$RNA_snn_res.0.6)
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$cellsubgroups <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'cellsubgroups', repel = TRUE)
preduction summary Cell_Types
0 neurons Neurons 1 NPC/oligo/astro/neurons/RG Neurons 2 neurons Neurons 3 NPC/RG/Astro Neurons 4 RG/endo RG 5 DA neurons Neurons 6 Neurons Neurons 7 DA neurons Neurons 8 RG/endo Endothelial 9 RG/astro Astro 10 opc/npc/astro/neurons Neurons 11 neurons/dividing/RG Neurons
Clustering higher res res 1.2 0 Neurons 1 Neurons 2 Neurons 3 Astro/RG/Neurons 4 RG/Astro/Neurons 5 RG/Neurons/NPC 6 RG/NPC/Endo 7 Neurons DA 8 Neurons 9 Neurons DA 10 Neurons DA 11 RG/Endo 12 RG/Astro 13 RG/Astro/Neurons 14 Neurons/RG
Idents(seu.q) <- 'RNA_snn_res.1.2'
# highlight the DA neurons
cluster.ids <- c("Neurons","Neurons","NPC",
"Neurons","Neurons","Neurons",
"Neurons DA","Neurons","Neurons DA",
"Neurons DA", "Neurons DA","Endothelial",
"RG/Astro","RG/Astro/Neurons","Neurons/RG")
cluster.ids <- c("Neurons","Neurons","Neurons",
"Neurons","Neurons","NPC",
"Neurons","Neurons","Neurons",
"Neurons", "Neurons","Endothelial",
"Astrocytes","Radial Glia","Radial Glia")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_Types <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_Types', repel = TRUE)

#DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'RNA_snn_res.1.2', repel = TRUE)
#clustree(seu.q)
# save the Neurons2 with labels
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Neurons2LabelsSeu30092022.RDS")
FACS population Glia1 (should be astrocytes)
# explore filtering
seu <- Glia1
seu
#
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 1000)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 500)
VlnPlot(seu, pt.size = 0.10, features = c("nCount_RNA"), y.max = 2000)
# filter more cells
seu.ft <- subset(seu, subset = nFeature_RNA > 300 & nCount_RNA > 500 & nCount_RNA < 10000)
seu.ft
# still a lot of cells 47295
# will likely remove a lot more with the doublet finder
saveRDS(seu.ft, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1AstroSeu01102022.RDS")
seu.ft <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1AstroSeu01102022.RDS")
seu.ft <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1AstroSeu01102022.RDS")
Doublet finder
suppressMessages(require(DoubletFinder))
# filtering out MALAT1 and mitochondrial genes
seu.ft <- seu.ft[!grepl("MALAT1", rownames(seu.ft)), ]
seu.ft <- seu.ft[!grepl("^MT-", rownames(seu.ft)), ]
# like in the tutorial I'm following MALAT1 is the top most expressed gene. The top genes are a lot of MT and Ribosomal genes
seu.ft[["percent.rb"]] <- PercentageFeatureSet(seu.ft, pattern = "^RP")
# down sample there are too many cells to run doublet finder
seu.sub <- subset(seu.ft, downsample = 20000)
seu.d = NormalizeData(seu.sub)
seu.d = FindVariableFeatures(seu.d, verbose = F)
seu.d = ScaleData(seu.d, vars.to.regress = c("nFeature_RNA", "percent.mt"),
verbose = F)
seu.d = RunPCA(seu.d, verbose = F, npcs = 15)
seu.d = RunUMAP(seu.d, dims = 1:10, verbose = F)
nExp <- round(ncol(seu.d) * 0.15) # expect more doublets because there is a lot more cells
seu.d <- doubletFinder_v3(seu.d, pN = 0.25, pK = 0.09, nExp = nExp, PCs = 1:10)
# the memory limit is reached here - I could run on compute canada
# For now I'll downsample
# this works
# name of the DF prediction can change, so extract the correct column name.
DF.name = colnames(seu.d@meta.data)[grepl("DF.classification", colnames(seu.d@meta.data))]
cowplot::plot_grid(ncol = 2, DimPlot(seu.d, group.by = "orig.ident") + NoAxes(),
DimPlot(seu.d, group.by = DF.name) + NoAxes())
VlnPlot(seu.d, features = "nFeature_RNA", group.by = DF.name, pt.size = 0.1)
Remove the doublet cells
seu.d <- seu.d[, seu.d@meta.data[, DF.name]== "Singlet"]
dim(seu.d)
dim(seu.sub)
# 20000 pre filter
# creates the expected percentage
Repeat workflow with doublet removed data and find clusters for
seu <- NormalizeData(seu.d, normalization.method = "LogNormalize", scale.factor = 10000)
seu <- FindVariableFeatures(seu, selection.method = "vst", nfeatures = 2000)
seu <- ScaleData(seu)
seu <- RunPCA(seu)
seu <- RunUMAP(seu, reduction = "pca", n.neighbors = 43, dims = 1:30)
DimPlot(seu, reduction = "umap")
seu.q <- FindNeighbors(seu, dims = 1:25, k.param = 43)
seu.q <- FindClusters(seu.q, resolution = c(0,0.2,0.4,0.6))
seu.q <- FindClusters(seu.q, resolution = c(0,0.05,0.1,0.8))
library(clustree)
clustree(seu.q)
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.05')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.1')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.2')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.4')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.6')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.8')
Look at some expression markers in a feature plot
# genes reported up in Astrocytes
FeaturePlot(seu.q, features = c("GFAP","S100B","AQP4","SLC1A3","GJA1",
"APOE","TEAD1","GSTA4","SOX9",
"VIM","HMG20A","ALDH1L1"))
# almost no GFAP expression and lots of S100B everywhere
Predict cell types
# SNCA and control midbrain organoids 165 days in culture
MBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AST23_BrainComm/MBOclusters_names29072021.rds")
# Midbrain AIW002 120 days in culture
AIWMBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio120days/MOintegratedClusterK123res0.8.names_nov16_2021")
# Midbrain AIW002 60 days in culture
AIW60 <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio60days/AWI002ParkinKOPinkKO60days_labels_14052022.rds")
#first predict with the MBO data
Idents(MBO) <- "cluster_labels"
DefaultAssay(MBO) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = MBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = MBO$cluster_labels)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAST23.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAST23.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$MBOAST23.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# clusters don't break up by the predicted cell types
############ another predictions now using the AIW organoids
Idents(AIWMBO) <- "res08names"
DefaultAssay(AIWMBO) <- "RNA"
anchors <- FindTransferAnchors(reference = AIWMBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIWMBO$res08names)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAIW.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAIW.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$MBOAIW.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# the predicted cell types make more sense from the AIW002 organoid
# now predict with the AIW002 60 days organoid
Idents(AIW60) <- "cluster.ids"
DefaultAssay(AIW60) <- "RNA"
anchors <- FindTransferAnchors(reference = AIW60, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIW60$cluster.ids)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$AIW60.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW60.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$AIW60.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# save ojbect with predicitons
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1PredictionsSeu01102022.RDS")
Predict with the brain scRNAseq
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_midbrain_striatum.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 752 anchors
Filtering anchors
Retained 108 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Astrocyte_4 Endo_4 Neuron_98 RG_1
7791 3019 2 9188
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.stri.pred <- Idents(seu.q)
print(table(seu.q$Bha.mid.stri.pred))
Astrocyte_4 Endo_4 RG_1 Neuron_98
7791 3019 9188 2
DimPlot(seu.q, group.by = 'Bha.mid.stri.pred')

DimPlot(seu.q, group.by = 'subgroups')

# do the predictions differ with the main cell type groups instead of the cluster in the reference data?
Idents(seu.r) <- "cell_type"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 752 anchors
Filtering anchors
Retained 108 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_type)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
astrocyte endothelial neuron radial glia
3747 7012 2152 7089
DimPlot(seu.q, group.by = 'predicted.id')

# AIW002 120 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$MBOAIW.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW120 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw120 <- top.pred.celltype.AIW120[order(top.pred.celltype.AIW120$Var1,-top.pred.celltype.AIW120$Freq),]
row.names(df.top.aiw120) <- NULL
df.top.aiw120$I <- row.names(df.top.aiw120)
# AIW002 60 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$AIW60.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW60 <-as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw60 <- top.pred.celltype.AIW60[order(top.pred.celltype.AIW60$Var1,-top.pred.celltype.AIW60$Freq),]
row.names(df.top.aiw60) <- NULL
df.top.aiw60$I <- row.names(df.top.aiw60)
# AST23 165 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$MBOAST23.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AST23 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.AST23 <- top.pred.celltype.AST23[order(top.pred.celltype.AST23$Var1,-top.pred.celltype.AST23$Freq),]
row.names(df.top.AST23) <- NULL
df.top.AST23$I <- row.names(df.top.AST23)
### add in the prediction from brain data Bhaduri midbrain and striatum
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$Bha.mid.stri.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha <- top.pred.celltype.Bha[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha) <- NULL
df.top.Bha$I <- row.names(df.top.Bha)
## these are calculated below
### add in the prediction from brain whole brain data Bhaduri midbrain down sampled
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$Bha))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha1 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha1 <- top.pred.celltype.Bha1[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha1) <- NULL
df.top.Bha1$I <- row.names(df.top.Bha1)
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table <- merge(pred.table, df.top.Bha1, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha1, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
NA
These predictions are not good. There are several astrocyte markers by expression levels. Everything is predicted as Radial glia or oligo dendrocytes
Try to predict with the whole brain and see if it’s different
Idents(seu.q) <- 'predicted.id'
seu.q$Bha <- Idents(seu.q)
print(table(seu.q$Bha))
Oligo_9 Endo_1 GW20_Astrocyte_39 Astrocyte_4 Endo_2 Astrocyte_1
692 415 1312 8401 4497 2308
Astrocyte_2 Endo_3 Neuron_37 Dividing_5 IPC_34 IPC_29
159 1621 521 24 35 1
GW18_2_45Oligo Dividing_1 Oligo_8 GW19_2_45Outlier Oligo_6
2 5 4 2 1
DimPlot(seu.q, group.by = 'Bha')

DimPlot(seu.q, group.by = 'subgroups')

Try to predict with the astrocyte Kamath data
astro.ref <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/Macosko_Data/PD_astro.Rds")
# need to make PCA and UMAP
astro.ref <- NormalizeData(astro.ref)
astro.ref <- FindVariableFeatures(astro.ref, selection.method = "vst", nfeatures = 2000)
astro.ref <- ScaleData(astro.ref)
astro.ref <- RunPCA(astro.ref)
astro.ref <- RunUMAP(astro.ref, reduction = "pca", n.neighbors = 205, dims = 1:25)
colnames(astro.ref@meta.data)
Idents(astro.ref) <- "Cell_Subtype"
DefaultAssay(astro.ref) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = astro.ref ,query = seu.q, dims = 1:20)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = astro.ref$Cell_Subtype, k.weight = 10)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$astro.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'astro.pred', label = TRUE)
table(seu.q$astro.pred)
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, NA)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
seu.q$astro.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'astro.pred.thresh', label = TRUE)
table(seu.q$astro.pred.thresh)
# 19986 Astro_VIM_TNFSRF12A no threshold Astro_GLYATL2 14
# 8376 Astro_VIM_TNFSRF12A with 95% threshold
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
Possible predicted in other clusters
clustree(seu.q)

Make the prediction table for high resolution Res 0.8 12 clusters
# AIW002 120 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$MBOAIW.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW120 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw120 <- top.pred.celltype.AIW120[order(top.pred.celltype.AIW120$Var1,-top.pred.celltype.AIW120$Freq),]
row.names(df.top.aiw120) <- NULL
df.top.aiw120$I <- row.names(df.top.aiw120)
# AIW002 60 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$AIW60.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW60 <-as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw60 <- top.pred.celltype.AIW60[order(top.pred.celltype.AIW60$Var1,-top.pred.celltype.AIW60$Freq),]
row.names(df.top.aiw60) <- NULL
df.top.aiw60$I <- row.names(df.top.aiw60)
# AST23 165 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$MBOAST23.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AST23 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.AST23 <- top.pred.celltype.AST23[order(top.pred.celltype.AST23$Var1,-top.pred.celltype.AST23$Freq),]
row.names(df.top.AST23) <- NULL
df.top.AST23$I <- row.names(df.top.AST23)
### add in the prediction from brain data Bhaduri midbrain and striatum
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$Bha.mid.stri.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha <- top.pred.celltype.Bha[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha) <- NULL
df.top.Bha$I <- row.names(df.top.Bha)
## these are calculated below
### add in the prediction from brain whole brain data Bhaduri midbrain down sampled
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$Bha))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha1 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha1 <- top.pred.celltype.Bha1[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha1) <- NULL
df.top.Bha1$I <- row.names(df.top.Bha1)
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table <- merge(pred.table, df.top.Bha1, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha1, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
NA
Look at cluster markers
Idents(seu.q) <- 'RNA_snn_res.0.2'
ClusterMarkers <- FindAllMarkers(seu.q, only.pos = TRUE)
top5 <- ClusterMarkers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
write.csv(ClusterMarkers,"/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/Glia1AstrocytesClusterMarkers_new.csv")
# for the res 0.6 the largers groups 0 and 1 don't have great markers and none of the markers are really very good.
# I'll rerun with res 0.2
unique(seu.q$RNA_snn_res.0.2)
# still not much better
Check cell type markers with EnrichR
library(enrichR)
setEnrichrSite("Enrichr") # Human genes
# list of all the databases
# libaries with cell types
db <- c('Allen_Brain_Atlas_up','Descartes_Cell_Types_and_Tissue_2021',
'CellMarker_Augmented_2021','Azimuth_Cell_Types_2021')
# enrichr(genes, databases = NULL)
#I'll run the clusters one at a time
N1.c0 <- ClusterMarkers %>% filter(cluster == 5 & avg_log2FC > 0)
genes <- N1.c0$gene
N1.c0.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c0.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c0.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c0.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c0.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c0.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 0 - Cell type marker library - Brain astrocyte top hit and embryonic astrocytes
# gene list in term: brain astrocyte EFEMP1;NFIA;LIX1;PSAP;KIF21A;S100B;CRYAB;DKK3
# embryo astrocytes SOX2;BEX1;HMGCS1;PTPRZ1;LIX1;S100B;DKK3;ITM2C
# cluster 1 - stem cell pericyte (brain), stelate, astrocyte
# cluster 2 - hypothalmus, endothelial cells, macrophage
# endothelial CSTB;PRELID1;MT1X;CRIP2;RHOC;TMEM141;MT2A;RPS28;CCDC85B;EIF3I;RBP1;ID1;C4ORF3;ID3;PCBD1;MSX1;PPIC
# cluster 3 - smooth muscle cells
# cluster 4 - NK cells, fibroblasts
# NK cells ITGB1;RAB5C;GSTP1;PDCD5;EEF1B2;TGOLN2;SDCBP;MT2A;LDHA;SNRPD2;YWHAQ;ZNF326;TMSB10;CCDC50
# fibroblasts COL3A1;CALD1;COL6A3
# cluster 5 - endothelial cells, NK cells, CD8+
# cluster 6 - stromal cells eurythroblasts, none-neuronal, oligo
# reran and now there are only 5 clusters
# repeat checking
Cluster 0 - astrocytes Cluster 1 - pericyte astrocyte (weak still) Cluster 2 - endothelial Cluster 3 - smooth muscle Cluster 4 - NK/fibroblast Cluster 5 - endothelial Cluster 6 - non- neuronal
VlnPlot(seu.q, features = c("CD44","ITGB1","S100B"), group.by = 'orig.ident')
VlnPlot(seu.ft, features = c("CD44","ITGB1","S100B"), group.by = 'orig.ident')
Check expression of known markers
Idents(seu.q) <- 'RNA_snn_res.0.2'
feature_list = c("MKI67","SOX2","POU5F1","DLX2","PAX6","SOX9","HES1","NES","RBFOX3","MAP2","NCAM1","CD24","GRIA2","GRIN2B","GABBR1","GAD1","GAD2","GABRA1","GABRB2","TH","ALDH1A1","LMX1B","NR4A2","CORIN","CALB1","KCNJ6","CXCR4","ITGA6","SLC1A3","CD44","AQP4","S100B", "PDGFRA","OLIG2","MBP","CLDN11","VIM","VCAM1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = feature_list) +RotatedAxis()
PD_poulin = c("TH","SLC6A3","SLC18A2","SOX6","NDNF","SNCG","ALDH1A1","CALB1","TACR2","SLC17A6","SLC32A1","OTX2","GRP","LPL","CCK","VIP")
DoHeatmap(seu.q, features = PD_poulin, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = PD_poulin)+RotatedAxis()
ealryNeur = c("DCX","NEUROD1","TBR1")
proliferation = c("PCNA","MKI67")
neuralstem = c("SOX2","NES","PAX6","MASH1")
feature_list <- c("DCX","NEUROD1","TBR1","PCNA","MKI67","SOX2","NES","PAX6","MASH1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = feature_list)+RotatedAxis()
mat_neuron = c("RBFOX3","SYP","DLG45","VAMP1","VAMP2","TUBB3","SYT1","BSN","HOMER1","SLC17A6")
# NeuN is FOX3 - RBFOX3
# PSD95 also SP-90 or DLG4
# VGLUT2 is SLC17A6
DoHeatmap(seu.q, features = mat_neuron, size=3, angle =90, group.bar.height = 0.02)
# cluster 4 also show mature neuron markers
DotPlot(seu.q, features = mat_neuron)+RotatedAxis()
# excitatory neuron markers
ex = c("GRIA2","GRIA1","GRIA4","GRIN1","GRIN2B","GRIN2A","GRIN3A","GRIN3","GRIP1","CAMK2A")
DoHeatmap(seu.q, features = ex, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ex)+RotatedAxis()
# inhibitory neuron markers
inh = c("GAD1","GAD2", "GAT1","PVALB","GABR2","GABR1","GBRR1","GABRB2","GABRB1","GABRB3","GABRA6","GABRA1","GABRA4","TRAK2")
DoHeatmap(seu.q, features = inh, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = inh)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
### glia markers
microglia = c("PTPRC","AIF1","ADGRE1") # ADGRE1 is a microglia marker F4/80, CD45 is PTPRC, gene name IBA1 is AIF1
astolgNPCpromicro = c("GFAP","S100B","SLC1A2","MBP","SOX10","SPP1","DCX","NEUROD1","TBR1","PCNA","MKI67","PTPRC","AIF1","ADGRE1")
# note GLT1 is EAAT2 which is SLC1A2 glutatmate transporter
# epithelial
epi = c("HES1","HES5","SOX2","SOX10","NES","CDH1","NOTCH1") # e-cadherin is CDH1
DoHeatmap(seu.q, features = astolgNPCpromicro, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = astolgNPCpromicro)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
DoHeatmap(seu.q, features = epi, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = epi)+RotatedAxis()
# also add Radial glia marker overlap with Glia and Neurons
features <- c("PTPRC","AIF1","ADGRE1", "VIM", "TNC","PTPRZ1","FAM107A","HOPX","LIFR",
"ITGB5","IL6ST")
DoHeatmap(seu.q, features = features, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = features)+RotatedAxis()
No TH expression
clusters 5 VIM highest, S100 B Cluster 4 Cluster 3 has some cells with high OTX2, NES indicates NPC/Precursors Cluster 2 has some SOX2 and PAX6 indicates NPC, VAMP2 indicating neurons, S100B highest and most - indicates astrocytes, also MBP indicates oligos Cluster 1 Cluster 0
Lable the clusters
Idents(seu.q) <- 'RNA_snn_res.0.2'
#seu.q <- BuildClusterTree(seu.q, reorder = TRUE, reorder.numeric = TRUE)
unique(seu.q$RNA_snn_res.0.2)
cluster.ids <- c("Astrocytes1","Astrocytes2","Precursors","RG1","RG2","Endothelial")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$subgroups <- Idents(seu.q)
#DimPlot(seu.q, group.by = 'RNA_snn_res.0.2', label = TRUE)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'subgroups', repel = TRUE)
# something weird is going on in the order
#saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1LabledSeu301102022.RDS")
Compare the Astrocyte groups and get some markers for sub groups
astro.sub.markers <- FindMarkers(seu.q, ident.1 = "Astrocytes1", ident.2 = "Astrocytes2", only.pos = FALSE)
#top5 <- astro.sub.markers %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = c("PLCG2","PTPRZ1","SNHG25","VCAN","LUM","DCN","S1004A"), size=3, angle =90, group.bar.height = 0.02)
astro2 <- rownames(astro.sub.markers %>% filter(avg_log2FC < 0.05))
DotPlot(seu.q, features = c("PLCG2","PTPRZ1","SNHG25","VCAN","LUM","DCN","S1004A")) + RotatedAxis()
DoHeatmap(seu.q, features = astro2[1:15], size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = astro2[1:15]) +RotatedAxis()
# radial glia subtyping
Idents(seu.q) <- ('subgroups')
rg.sub.markers <- FindMarkers(seu.q, ident.1 = "RG1", ident.2 = "RG2", only.pos = FALSE)
top5.up <- rg.sub.markers %>% top_n(n=10, wt = avg_log2FC)
top5.down <- rg.sub.markers %>% top_n(n=-10, wt = avg_log2FC)
ft <- rownames(top5.up)
DoHeatmap(seu.q, features = ft, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ft) + RotatedAxis()
ft <- rownames(top5.down)
DoHeatmap(seu.q, features = ft, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ft) + RotatedAxis()
npc.markers <- FindMarkers(seu.q, ident.1 = "Precursors", ident.2 = c("Astrocytes2","Astrocytes1"), only.pos = FALSE)
top10.npc <- npc.markers %>% top_n(n=10, wt = avg_log2FC)
npc.markers <- npc.markers %>% filter(avg_log2FC > 0)
dim(npc.markers)
ft <- rownames(top10.npc)
# consider naming precursors as astrocytes3
DoHeatmap(seu.q, features = ft, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ft) + RotatedAxis()
npc.markers.rg <- FindMarkers(seu.q, ident.1 = "Precursors", ident.2 = c("RG2","RG1"), only.pos = TRUE)
top10.npc <- npc.markers.rg %>% top_n(n=10, wt = avg_log2FC)
ft <- rownames(top10.npc)
# consider naming precursors as astrocytes3
DoHeatmap(seu.q, features = ft, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ft) + RotatedAxis()
## these have more differences from RG than Astrocytes
Add subtype gene ids
DimPlot(seu.q, group.by = 'RNA_snn_res.0.2')
cluster.ids <- c("Astrocytes-PLCG2","Astrocytes-DNC","Astrocytes-IGFBP2",
"RG1-CDKN1C","RG2-TYRP1","Endothelial")
#cluster.ids <- c("Astrocytes1","Astrocytes2","Precursors(Astrocytes)","RG1","RG2","Endothelial")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_types <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_types', repel = TRUE)
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1LabledSeu301102022.RDS")
# label with main cell type groups
DimPlot(seu.q, group.by = 'RNA_snn_res.0.2')
cluster.ids <- c("Astrocytes","Astrocytes","Astrocytes",
"RG","RG","Endothelial")
#cluster.ids <- c("Astrocytes1","Astrocytes2","Precursors(Astrocytes)","RG1","RG2","Endothelial")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_Type <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_Type', repel = TRUE)
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1LabledSeu301102022.RDS")
Label main cell types Res 0.8 predicitons 0 astro 1 Endo/RG/Astro 2 Astrocyte 3 RG/Endo/Astro 4 Astro 5 Astro 6 Epithela, endo, astro, RG 7 Astro 8 Astro 9 Astro/RG/Endo 10 Astro/RG 11 Astro/Neurons 12 Endo/RG.
#DimPlot(seu.q, group.by = 'RNA_snn_res.0.8')
Idents(seu.q) <- 'RNA_snn_res.0.8'
cluster.ids <- c("Astrocytes","Astrocytes-Endo-RG","Astrocytes",
"RG-Endo-Astro","Astro","Astro","Epi-Endo-Astro-RG",
"Astro","Astro","Astro-RG-Endo","Astro-RG","Astro-Neurons","Endo-RG")
cluster.ids <- c("Astrocytes","Astrocytes","Astrocytes",
"Astrocytes","Astrocytes","Astrocytes","Endothelial",
"Astrocytes","Astrocytes","Radial Glia","Radial Glia","Astrocytes","Endothelial")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_Type <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_Type', repel = TRUE)

saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1LabledSeu301102022.RDS")
table(seu.q$Cell_Type)
Astrocytes RG Endothelial Radial Glia
16068 2955 506 471
Quick check the Glial2
# explore filtering
seu <- Glia2
seu
#
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 1000)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 500)
VlnPlot(seu, pt.size = 0.10, features = c("nCount_RNA"), y.max = 2000)
# filter more cells
seu.ft <- subset(seu, subset = nFeature_RNA > 250 & nCount_RNA > 250 & nCount_RNA < 10000)
seu.ft
VlnPlot(seu.ft, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 2000)
VlnPlot(seu.ft.glia, features = c("CD44","S100B","ITGB1"))
VlnPlot(seu.ft, features = c("CD44","S100B","ITGB1"), group.by = 'orig.ident')
# both glia populations are similar
Levels seem similar in Glia1 and Glia2
Remove doublets and start to process Glia 2
suppressMessages(require(DoubletFinder))
# filtering out MALAT1 and mitochondrial genes
seu.ft <- seu.ft[!grepl("MALAT1", rownames(seu.ft)), ]
seu.ft <- seu.ft[!grepl("^MT-", rownames(seu.ft)), ]
# like in the tutorial I'm following MALAT1 is the top most expressed gene. The top genes are a lot of MT and Ribosomal genes
seu.ft[["percent.rb"]] <- PercentageFeatureSet(seu.ft, pattern = "^RP")
seu.d = NormalizeData(seu.ft)
seu.d = FindVariableFeatures(seu.d, verbose = F)
seu.d = ScaleData(seu.d, vars.to.regress = c("nFeature_RNA", "percent.mt"),
verbose = F)
seu.d = RunPCA(seu.d, verbose = F, npcs = 15)
seu.d = RunUMAP(seu.d, dims = 1:10, verbose = F)
nExp <- round(ncol(seu.d) * 0.08) # expect more doublets because there is a lot more cells
seu.d <- doubletFinder_v3(seu.d, pN = 0.25, pK = 0.09, nExp = nExp, PCs = 1:10)
# the memory limit is reached here - I could run on compute canada
# For now I'll downsample
# this works
# name of the DF prediction can change, so extract the correct column name.
DF.name = colnames(seu.d@meta.data)[grepl("DF.classification", colnames(seu.d@meta.data))]
cowplot::plot_grid(ncol = 2, DimPlot(seu.d, group.by = "orig.ident") + NoAxes(),
DimPlot(seu.d, group.by = DF.name) + NoAxes())
VlnPlot(seu.d, features = "nFeature_RNA", group.by = DF.name, pt.size = 0.1)
seu.d <- seu.d[, seu.d@meta.data[, DF.name]== "Singlet"]
dim(seu.d)
dim(seu.ft)
Cluster
seu <- NormalizeData(seu.d, normalization.method = "LogNormalize", scale.factor = 10000)
seu <- FindVariableFeatures(seu, selection.method = "vst", nfeatures = 2000)
seu <- ScaleData(seu)
seu <- RunPCA(seu)
seu <- RunUMAP(seu, reduction = "pca", n.neighbors = 25, dims = 1:30)
DimPlot(seu, reduction = "umap")
seu.q <- FindNeighbors(seu, dims = 1:25, k.param = 25)
seu.q <- FindClusters(seu.q, resolution = c(0,0.05,0.2,0.4,0.5,0.6,0.8))
library(clustree)
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.05')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.1')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.2')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.4')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.6')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.8')
clustree(seu.q)
# 0.4 is likely the best annotate subgroups
Predict cell types
# SNCA and control midbrain organoids 165 days in culture
MBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AST23_BrainComm/MBOclusters_names29072021.rds")
# Midbrain AIW002 120 days in culture
AIWMBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio120days/MOintegratedClusterK123res0.8.names_nov16_2021")
# Midbrain AIW002 60 days in culture
AIW60 <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio60days/AWI002ParkinKOPinkKO60days_labels_14052022.rds")
#first predict with the MBO data
Idents(MBO) <- "cluster_labels"
DefaultAssay(MBO) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = MBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = MBO$cluster_labels)
seu.q <- AddMetaData(seu.q, metadata = predictions)
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAST23.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAST23.pred', label = TRUE)
# see how accurate the predictions are
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "None")
Idents(seu.q) <- 'predicted.id'
seu.q$MBOAST23.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'predicted.id', label = TRUE)
table(seu.q$MBOAST23.pred)
table(seu.q$MBOAST23.thresh)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.4, seu.q$MBOAST23.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# clusters don't break up by the predicted cell types
############ another predictions now using the AIW organoids
Idents(AIWMBO) <- "res08names"
DefaultAssay(AIWMBO) <- "RNA"
anchors <- FindTransferAnchors(reference = AIWMBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIWMBO$res08names)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$AIW120.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW120.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.4, seu.q$MBOAIW.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# see how accurate the predictions are
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "None")
Idents(seu.q) <- 'predicted.id'
seu.q$AIW120.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW120.thresh', label = TRUE)
table(seu.q$AIW120.pred)
table(seu.q$AIW120.thresh)
# the predicted cell types make more sense from the AIW002 organoid
# now predict with the AIW002 60 days organoid
Idents(AIW60) <- "cluster.ids"
DefaultAssay(AIW60) <- "RNA"
anchors <- FindTransferAnchors(reference = AIW60, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIW60$cluster.ids)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$AIW60.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW60.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.4, seu.q$AIW60.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# see how accurate the predictions are
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "None")
Idents(seu.q) <- 'predicted.id'
seu.q$AIW60.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW60.thresh', label = TRUE)
table(seu.q$AIW60.pred)
table(seu.q$AIW60.thresh)
# most of the cells are predicted as NPCs in many populations
# save with predictions so far
#saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
seu.q <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
See how many cells are predicted as astrocytes with the threshold
DAsubtypes <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/Macosko_Data/DAsubgroups_processed.Rds")
Idents(astro.ref) <- "Cell_Subtype"
DefaultAssay(astro.ref) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = DAsubtypes ,query = seu.q, dims = 1:20)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = astro.ref$Cell_Subtype, k.weight = 10)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$astro.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'astro.pred', label = TRUE)
table(seu.q$astro.pred)
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
seu.q$astro.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'astro.pred.thresh', label = TRUE)
table(seu.q$astro.pred.thresh)
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
# a lot of these cells are also getting labelled as astrocytes
Do these get labelled as DA neurons too???
seu.q <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
DAsubtypes <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/Macosko_Data/DAsubgroups_processed.Rds")
Idents(DAsubtypes) <- "Cell_Subtype"
da.ref <- subset(DAsubtypes, downsample = 500)
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = da.ref, query = seu.q, dims = 1:20)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = da.ref$Cell_Subtype, k.weight = 10)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$da.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'da.pred', label = TRUE)
table(seu.q$da.pred)
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$da.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'da.pred.thresh', label = TRUE)
table(seu.q$da.pred.thresh)
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$da.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.da$Freq),]
row.names(df.top.astro) <- NULL
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
# after thresholding very few cells are predicted as neurons
{redicte with the brain scRNAseq
pathway <- "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/"
seu.q <- readRDS(paste(pathway,"Glia2LabledSeu03102022.RDS",sep = ""))
# midbrain and striatum
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_midbrain_striatum.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 881 anchors
Filtering anchors
Retained 303 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Astrocyte_4 Endo_11 Endo_4 Endo_7 Neuron_2 Neuron_37 Neuron_98
5810 4 506 186 41 6 396
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.stri.pred <- Idents(seu.q)
print(table(seu.q$Bha.mid.stri.pred))
Astrocyte_4 Neuron_2 Neuron_98 Endo_4 Endo_7 Endo_11 Neuron_37
5810 41 396 506 186 4 6
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'Bha.mid.pred.thresh', label = TRUE)

table(seu.q$Bha.mid.pred.thresh)
none Astrocyte_4
6942 7
DimPlot(seu.q, group.by = 'Bha.mid.pred.thresh')

# read in the reference dataset
# whole brain Bhaduri down sampled
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_downsample.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1844 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 1726 anchors
Filtering anchors
Retained 509 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Astrocyte_1 Astrocyte_2 Astrocyte_4 Astrocyte_6 Dividing_21
480 203 1310 2 136
Dividing_3 Endo_1 Endo_11 Endo_2 Endo_3
7 85 19 522 1
Endo_4 Endo_7 Endo_8 GW19_2_45Outlier GW19_Astrocyte_35
2 3 125 1604 1
GW20_Astrocyte_39 GW22both_RG_23 Interneuron_7 IPC_29 IPC_34
138 252 192 747 350
Microglia_6 Neuron_5 Neuron_93 Oligo_12 Oligo_9
5 6 253 11 1
RG_1 RG_11 RG_6
492 1 1
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.pred <- Idents(seu.q)
print(table(seu.q$Bha.pred))
Astrocyte_2 GW19_2_45Outlier Endo_8 IPC_29 IPC_34
203 1604 125 747 350
Interneuron_7 Dividing_21 Astrocyte_4 GW20_Astrocyte_39 Astrocyte_1
192 136 1310 138 480
Neuron_93 GW22both_RG_23 Endo_2 Endo_11 Endo_1
253 252 522 19 85
RG_1 Neuron_5 Endo_3 Microglia_6 Dividing_3
492 6 1 5 7
Oligo_12 RG_6 Endo_7 Endo_4 Astrocyte_6
11 1 3 2 2
GW19_Astrocyte_35 Oligo_9 RG_11
1 1 1
DimPlot(seu.q, group.by = 'Bha.pred')
DimPlot(seu.q, group.by = 'subgroups')
Compare predictions - make a predictions table
# AIW002 120 days predictions - take the thresholded options
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$AIW120.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW120 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw120 <- top.pred.celltype.AIW120[order(top.pred.celltype.AIW120$Var1,-top.pred.celltype.AIW120$Freq),]
row.names(df.top.aiw120) <- NULL
df.top.aiw120$I <- row.names(df.top.aiw120)
# AIW002 60 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$AIW60.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW60 <-as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw60 <- top.pred.celltype.AIW60[order(top.pred.celltype.AIW60$Var1,-top.pred.celltype.AIW60$Freq),]
row.names(df.top.aiw60) <- NULL
df.top.aiw60$I <- row.names(df.top.aiw60)
# AST23 165 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$MBOAST23.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AST23 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.AST23 <- top.pred.celltype.AST23[order(top.pred.celltype.AST23$Var1,-top.pred.celltype.AST23$Freq),]
row.names(df.top.AST23) <- NULL
df.top.AST23$I <- row.names(df.top.AST23)
# add the threshold Astro predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.celltype.astro[order(top.pred.celltype.astro$Var1,-top.pred.celltype.astro$Freq),]
row.names(df.top.astro) <- NULL
df.top.astro$I <- row.names(df.top.astro)
# add the neurons predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$da.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.da <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.da <- top.pred.celltype.da[order(top.pred.celltype.da$Var1,-top.pred.celltype.da$Freq),]
row.names(df.top.da) <- NULL
df.top.da$I <- row.names(df.top.da)
### add in the prediction from brain data Bhaduri midbrain and striatum
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$Bha.mid.stri.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha <- top.pred.celltype.Bha[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha) <- NULL
df.top.Bha$I <- row.names(df.top.Bha)
## these are calculated below
### add in the prediction from brain whole brain data Bhaduri midbrain down sampled
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$Bha.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha1 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha1 <- top.pred.celltype.Bha1[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha1) <- NULL
df.top.Bha1$I <- row.names(df.top.Bha1)
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table <- merge(pred.table, df.top.Bha1, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha1, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
NA
NA
NA
Predicted cluster annotations 0 Unknown/ NPC 1 RG 2 astro 3 RG 4 neurons 5 RG
Predict from developing cortex
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1389 features as input.
Projecting cell embeddings
Finding neighborhoods
Error in base::options(...future.oldOptions) : invalid argument
The dev cortex doesn’t predict Glia 2 well Try the developing forebrain
DefaultAssay(seu.r) <- 'RNA'
# clusters has subgroups
# levels are main cell groups
Idents(seu.r) <- "Level1"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1851 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 585 anchors
Filtering anchors
Retained 250 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$Level1)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
EarlyInhibitory Glioblast NeuralProgenitor RadialGlia VLMC
7 2893 1299 2630 120
Idents(seu.q) <- 'predicted.id'
seu.q$fb.pred <- Idents(seu.q)
print(table(seu.q$fb.pred))
RadialGlia Glioblast NeuralProgenitor EarlyInhibitory VLMC
2630 2893 1299 7 120
DimPlot(seu.q, group.by = 'fb.pred')

# add the threshold
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.80, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$fb.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'fb.thresh', label = TRUE)

table(seu.q$fb.thresh)
RadialGlia none VLMC NeuralProgenitor
1028 5659 113 149
# make the tables
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$fb.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top <- top.pred.celltype[order(top.pred.celltype$Var1,-top.pred.celltype$Freq),]
row.names(df.top) <- NULL
df.top$I <- row.names(df.top)
#VLMC is vascular and leptomeninges
# almost everything is predicted as 'none' when theshold is .95
# run again with lower threshold 0.8 and many are predicted as RG
# try predicting with the cluster labels
# later I can subset cell types for the reference data
Idents(seu.r) <- "Clusters"
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1851 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 585 anchors
Filtering anchors
Retained 250 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$Clusters)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Excitatory neurons possibly midbrain Glioblast/Pre-OPC
1947 4
OPCs Radial Glia VLMC primed?
1 2931
Radial glia/Glioblast/Forebrain progenitor EMX1 Striatum/Cortical neurons
75 1871
VLMCs
120
Idents(seu.q) <- 'predicted.id'
seu.q$fb.sub.pred <- Idents(seu.q)
print(table(seu.q$fb.sub.pred))
Radial Glia VLMC primed? Excitatory neurons possibly midbrain
2931 1947
Striatum/Cortical neurons Radial glia/Glioblast/Forebrain progenitor EMX1
1871 75
VLMCs OPCs
120 1
Glioblast/Pre-OPC
4
DimPlot(seu.q, group.by = 'fb.sub.pred')

# add the threshold
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.80, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$fb.sub.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'fb.sub.thresh', label = TRUE)

table(seu.q$fb.sub.thresh)
Radial Glia VLMC primed? none
1015 5727
VLMCs Excitatory neurons possibly midbrain
113 94
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$fb.sub.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(4, Freq))
df.top <- top.pred.celltype[order(top.pred.celltype$Var1,-top.pred.celltype$Freq),]
row.names(df.top) <- NULL
df.top$I <- row.names(df.top)
df.top.ft <- df.top %>% filter(Freq > 0)
Predictions with thresholds:
0 - RG 1 - RG 2 - none 3 - RG 4 - Neural precursor 5 - VLMC (vascular and leptomeninges)
Look at gene lists with known markers
Idents(seu.q) <- 'RNA_snn_res.0.2'
# many cell types list
feature_list = c("MKI67","SOX2","POU5F1","DLX2","PAX6","SOX9","HES1","NES","RBFOX3","MAP2","NCAM1","CD24","GRIA2","GRIN2B","GABBR1","GAD1","GAD2","GABRA1","GABRB2","TH","ALDH1A1","LMX1B","NR4A2","CORIN","CALB1","KCNJ6","CXCR4","ITGA6","SLC1A3","CD44","AQP4","S100B", "PDGFRA","OLIG2","MBP","CLDN11","VIM","VCAM1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = feature_list) +RotatedAxis()
# Dopaminergic markers
PD_poulin = c("TH","SLC6A3","SLC18A2","SOX6","NDNF","SNCG","ALDH1A1","CALB1","TACR2","SLC17A6","SLC32A1","OTX2","GRP","LPL","CCK","VIP")
DoHeatmap(seu.q, features = PD_poulin, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = PD_poulin)+RotatedAxis()
ealryNeur = c("DCX","NEUROD1","TBR1")
proliferation = c("PCNA","MKI67")
neuralstem = c("SOX2","NES","PAX6","MASH1")
feature_list <- c("DCX","NEUROD1","TBR1","PCNA","MKI67","SOX2","NES","PAX6","MASH1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = feature_list)+RotatedAxis()
mat_neuron = c("RBFOX3","SYP","DLG45","VAMP1","VAMP2","TUBB3","SYT1","BSN","HOMER1","SLC17A6")
# NeuN is FOX3 - RBFOX3
# PSD95 also SP-90 or DLG4
# VGLUT2 is SLC17A6
DoHeatmap(seu.q, features = mat_neuron, size=3, angle =90, group.bar.height = 0.02)
# cluster 4 also show mature neuron markers
DotPlot(seu.q, features = mat_neuron)+RotatedAxis()
# excitatory neuron markers
ex = c("GRIA2","GRIA1","GRIA4","GRIN1","GRIN2B","GRIN2A","GRIN3A","GRIN3","GRIP1","CAMK2A")
DoHeatmap(seu.q, features = ex, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ex)+RotatedAxis()
# inhibitory neuron markers
inh = c("GAD1","GAD2", "GAT1","PVALB","GABR2","GABR1","GBRR1","GABRB2","GABRB1","GABRB3","GABRA6","GABRA1","GABRA4","TRAK2")
DoHeatmap(seu.q, features = inh, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = inh)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
### glia markers
microglia = c("PTPRC","AIF1","ADGRE1") # ADGRE1 is a microglia marker F4/80, CD45 is PTPRC, gene name IBA1 is AIF1
astolgNPCpromicro = c("GFAP","S100B","SLC1A2","MBP","SOX10","SPP1","DCX","NEUROD1","TBR1","PCNA","MKI67","PTPRC","AIF1","ADGRE1")
# note GLT1 is EAAT2 which is SLC1A2 glutatmate transporter
# epithelial
epi = c("HES1","HES5","SOX2","SOX10","NES","CDH1","NOTCH1") # e-cadherin is CDH1
DoHeatmap(seu.q, features = astolgNPCpromicro, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = astolgNPCpromicro)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
DoHeatmap(seu.q, features = epi, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = epi)+RotatedAxis()
# also add Radial glia marker overlap with Glia and Neurons
features <- c("PTPRC","AIF1","ADGRE1", "VIM", "TNC","PTPRZ1","FAM107A","HOPX","LIFR",
"ITGB5","IL6ST")
DoHeatmap(seu.q, features = features, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = features)+RotatedAxis()
# radial glia markers
rg <- c("VIM","NES","PAX6","HES1","EAAT1","NCAD1","SOX2","FABP7")
DoHeatmap(seu.q, features = rg, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = rg)+RotatedAxis()
# NPC and radial glia are very similar
Marker expression predictions Cluster 0 - unknown Cluster 1 - RG Cluster 2 - unknown Cluster 3 - RG cluster 4 - immature neurons Cluster 5 - RG, opc
Check the levels of RNA in each cluster
VlnPlot(seu.q, features = "nFeature_RNA")
Cluster 0 and 2 have fewer sequences than other groups and thus no markers Possibly remove these is they don’t come up with some markers
Find cluster markers
Idents(seu.q) <- 'RNA_snn_res.0.2'
ClusterMarkers <- FindAllMarkers(seu.q, only.pos = TRUE)
top5 <- ClusterMarkers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
#write.csv(ClusterMarkers,"/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/Glia2RGClusterMarkers_new.csv")
Idents(seu.q) <- 'RNA_snn_res.0.1'
ClusterMarkers <- FindAllMarkers(seu.q, only.pos = TRUE)
top5 <- ClusterMarkers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
Markers of 2 are matching with 5 possibly merge these together Cluster 0 markers don’t look up regulated but the list is long
Look at the libraries
library(enrichR)
setEnrichrSite("Enrichr") # Human genes
# list of all the databases
# libaries with cell types
db <- c('Descartes_Cell_Types_and_Tissue_2021',
'CellMarker_Augmented_2021','Azimuth_Cell_Types_2021')
# enrichr(genes, databases = NULL)
#I'll run the clusters one at a time
N1.c0 <- ClusterMarkers %>% filter(cluster == 0 & avg_log2FC > 0)
genes <- N1.c0$gene
N1.c0.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c0.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c0.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c0.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c0.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
The adult brain doesn’t predict radial glia - there are radial glia but I believe these are different from the ‘neuroblast’ type radial glia
I will use a developing brain reference
DimPlot(seu.q)
seu.q <- readRDS(paste(pathway,"Glia2LabledSeu03102022.RDS",sep = ""))
# midbrain and striatum
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_midbrain_striatum.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.stri.pred <- Idents(seu.q)
print(table(seu.q$Bha.mid.stri.pred))
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'Bha.mid.pred.thresh', label = TRUE)
table(seu.q$Bha.mid.pred.thresh)
DimPlot(seu.q, group.by = 'Bha.mid.pred.thresh')
# read in the reference dataset
# whole brain Bhaduri down sampled
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_downsample.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.pred <- Idents(seu.q)
print(table(seu.q$Bha.pred))
DimPlot(seu.q, group.by = 'Bha.pred')
DimPlot(seu.q, group.by = 'subgroups')
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'Bha.pred.thresh', label = TRUE)
table(seu.q$Bha.mid.pred.thresh)
DimPlot(seu.q, group.by = 'Bha.pred.thresh')
Add some cell type annotations
Idents(seu.q) <- 'RNA_snn_res.0.2'
cluster.ids <- c("Glia1","RG1","Glia2","RG2","NeuronsImmature","RG3")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$subgroups <- Idents(seu.q)
#DimPlot(seu.q, group.by = 'RNA_snn_res.0.2', label = TRUE)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'subgroups', repel = TRUE)
# save file
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
Main cell groups
Idents(seu.q) <- 'RNA_snn_res.0.2'
cluster.ids <- c("Radial Glia","Radial Glia","Radial Glia","Radial Glia","NPC","Other")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_Types <- Idents(seu.q)
#DimPlot(seu.q, group.by = 'RNA_snn_res.0.2', label = TRUE)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_Types', repel = TRUE)

saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
Proportions of cell types
table(seu.q$Cell_Types)
dim(seu.q)
prp <- as.data.frame(table(seu.q$Cell_Types))
prp
prp$prop <- prp$Freq/sum(prp$Freq)*100
prp$Sample <- 'RadialGlia'
prp
I’ll calculate the proportions for each cell type and make a table or plot in the comparison workbook.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpTaW5nbGUgY2VsbCBzZXEgYWZ0ZXIgc29ydGluZyBmb3IgUGhlbm9JRAoKc2FtcGxlMSA9IE5ldXJvbnMxCnNhbXBsZTIgPSBOZXVyb25zMgpzYW1wbGUzID0gR2xpYTEgLSBBc3Ryb2N5dGVzIChDRDQ0KykKc2FtcGxlNCA9IEdsaWEyIC0gUmFkaWFsIEdsaWEgKENENDQtKQoKSW4gSFBDIEkgaGF2ZSBydW4gc3RlcHMgb2Ygc2NybmFib3ggKGN1c3RvbSBwaXBlbGluZSBpbiBwcm9ncmVzcykKMS4gQ2VsbCBSYW5nZXIgZm9yIGZlYXR1cmUgc2VxCjIuIENyZWF0ZSBTZXVyYXQgT2JqZWN0cyAKMy4gQXBwbHkgbWluaW11bSBmaWx0ZXJpbmcgYW5kIGNhbGN1bGF0ZSBwZXJjZW50IG1pdG9jaG9uZHJpYS4KCkkgaGF2ZSB0ZWNobmljYWwgMyByZXBsaWNhdGVzIHdpdGggaGFzaHRhZyBsYWJlbHMgYXQgdGhpcyBwb2ludCBJIGhhdmVuJ3QgeWV0IGRlbXVsdGlwbGV4IHRoZSBoYXNodGFncy4gVGhlIGRhdGEgaGVyZSB3aWxsIGJlIHRyZWF0ZWQgYXMgb25lIHNhbXBsZS4gIEkgc29ydGVkIHRocmVlIHNlcGFyYXRlIHNhbXBsZXMgYW5kIHBvb2xlZCB0aGVtIHRvZ2V0aGVyLiAKCgpgYGB7cn0KIyBzZXQgdXAgdGhlIGVudmlyb25tZW50CgpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkcGx5cikKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZ2dwbG90MikKCiNybShsaXN0ID0gbHMoKSkKCnNldSA8LSBBZGRNZXRhRGF0YShzZXUsICkKCgoKYGBgCgoKUmVhZCBpbiB0aGUgc2V1cmF0IG9iamVjdHMgbWFkZSBpbiBjb21wdXRlIGNhbmFkYQoKYGBge3J9CgojIHRoaXMgc2VlbXMgdG8gbmV2ZXIgbG9hZCBJJ2xsIHVzZSBzdGVwIDMgb3V0cHV0IHRoYXQgaGFzIHNvbWUgZmlsdGVyaW5nIAojIG5GZWF0dXJlX1JOQSA+IDE4MCBhbmQgcGVyY2VudC5tdCA8IDI1CgpwYXRod2F5IDwtICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvIgoKTmV1cm9uczEgPC0gcmVhZFJEUyhwYXN0ZShwYXRod2F5LCJzZXUxLnJkcyIsc2VwID0gIiIpKQpOZXVyb25zMiA8LSByZWFkUkRTKHBhc3RlKHBhdGh3YXksInNldTIucmRzIixzZXAgPSAiIikpCkdsaWExIDwtIHJlYWRSRFMocGFzdGUocGF0aHdheSwic2V1My5yZHMiLHNlcCA9ICIiKSkKR2xpYTIgPC0gcmVhZFJEUyhwYXN0ZShwYXRod2F5LCJzZXU0LnJkcyIsc2VwID0gIiIpKQoKTmV1cm9uczEKTmV1cm9uczIKR2xpYTEKR2xpYTIKCgpgYGAKCgpIYXZlIGEgbG9vayBhdCB0aGUgb2JqZWN0cyB0aGF0IGFscmVhZHkgaGF2ZSBzb21lIGZpbHRlcmluZwoKClNlZSB0aGUgdmlvbGluIHBsb3RzIAoKYGBge3J9CgpWbG5QbG90KE5ldXJvbnMxLCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIG5jb2wgPSAzKQoKVmxuUGxvdChOZXVyb25zMSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiksIHkubWF4ID0gNTAwKQpWbG5QbG90KE5ldXJvbnMxLCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuQ291bnRfUk5BIiksIHkubWF4ID0gMjAwMCkKCgojIGZpbHRlciBtb3JlIGNlbGxzCgpOZXVyb24xLmZ0IDwtIHN1YnNldChOZXVyb25zMSwgc3Vic2V0ID0gbkZlYXR1cmVfUk5BID4gMjUwICYgbkNvdW50X1JOQSA+IDI1MCAmIG5Db3VudF9STkEgPCAxMDAwMCkgCk5ldXJvbjEuZnQKCiMgMzM1NDEgZmVhdHVyZXMgYWNyb3NzIDE4MzMgc2FtcGxlcwoKCmBgYAoKCkZpbHRlcnMgb3V0IHNwZWNpZmljIGdlbmVzCgpgYGB7cn0KCiMgZmlsdGVyIG91dCBNQUxBVDEgc3VwZXIgaGlnaCBleHByZXNzaW9uCgpzZXUuZnQgPC0gc2V1WyFncmVwbCgiTUFMQVQxIiwgcm93bmFtZXMoc2V1KSksIF0Kc2V1LmZ0IDwtIHNldS5mdFshZ3JlcGwoIl5NVC0iLCByb3duYW1lcyhzZXUuZnQpKSwgXQoKIyB0aGlzIGZpbHRlcmVkIG9iamVjdCBtaWdodCBjbHVzdGVyIGRpZmZlcmVudGx5CgoKYGBgCgpQQ0EgYW5kIFVNQVAKCmBgYHtyfQoKc2V1LnEgPC0gTm9ybWFsaXplRGF0YShzZXUuZnQsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwKQpzZXUucSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzZXUucSwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQpzZXUucSA8LSBTY2FsZURhdGEoc2V1LnEpCnNldS5xIDwtIFJ1blBDQShzZXUucSkKc2V1LnEgPC0gUnVuVU1BUChzZXUucSwgcmVkdWN0aW9uID0gInBjYSIsIG4ubmVpZ2hib3JzID0gMjUsIGRpbXMgPSAxOjMwLCBtaW4uZGlzdCA9IDAuMjUsIHNwcmVhZCA9IDIpCgpgYGAKCgpUcnkgdG8gZmluZCBkb3VibGV0cyB3aXRoIGRvdWJsZXQgZmluZGVyCgpgYGB7cn0KcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoJ2NocmlzLW1jZ2lubmlzLXVjc2YvRG91YmxldEZpbmRlcicpCnN1cHByZXNzTWVzc2FnZXMocmVxdWlyZShEb3VibGV0RmluZGVyKSkKCgpgYGAKCmBgYHtyfQoKc2V1LmQgPSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzZXUucSwgdmVyYm9zZSA9IEYpCnNldS5kID0gU2NhbGVEYXRhKHNldS5kLCB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCJuRmVhdHVyZV9STkEiLCAicGVyY2VudC5tdCIpLAogICAgdmVyYm9zZSA9IEYpCnNldS5kID0gUnVuUENBKHNldS5kLCB2ZXJib3NlID0gRiwgbnBjcyA9IDIwKQpzZXUuZCA9IFJ1blVNQVAoc2V1LmQsIGRpbXMgPSAxOjEwLCB2ZXJib3NlID0gRikKCm5FeHAgPC0gcm91bmQobmNvbChzZXUuZCkgKiAwLjA2KSAgIyBleHBlY3QgNiUgZG91YmxldHMKc2V1LmQgPC0gZG91YmxldEZpbmRlcl92MyhzZXUuZCwgcE4gPSAwLjI1LCBwSyA9IDAuMDksIG5FeHAgPSBuRXhwLCBQQ3MgPSAxOjEwKQoKCiMgbmFtZSBvZiB0aGUgREYgcHJlZGljdGlvbiBjYW4gY2hhbmdlLCBzbyBleHRyYWN0IHRoZSBjb3JyZWN0IGNvbHVtbiBuYW1lLgpERi5uYW1lID0gY29sbmFtZXMoc2V1LmRAbWV0YS5kYXRhKVtncmVwbCgiREYuY2xhc3NpZmljYXRpb24iLCBjb2xuYW1lcyhzZXUuZEBtZXRhLmRhdGEpKV0KCgoKY293cGxvdDo6cGxvdF9ncmlkKG5jb2wgPSAyLCBEaW1QbG90KHNldS5kLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikgKyBOb0F4ZXMoKSwKICAgIERpbVBsb3Qoc2V1LmQsIGdyb3VwLmJ5ID0gREYubmFtZSkgKyBOb0F4ZXMoKSkKCgpgYGAKCkRvIHRoZSBkb3VibGUgY2VsbHMgaGF2ZSBtb3JlIGdlbmVzIHRoYW4gdGhlIHNpbmdsZXQ/PwoKYGBge3J9CgpWbG5QbG90KHNldS5kLCBmZWF0dXJlcyA9ICJuRmVhdHVyZV9STkEiLCBncm91cC5ieSA9IERGLm5hbWUsIHB0LnNpemUgPSAwLjEpCiMgeWVzIAoKYGBgCgpSZW1vdmUgdGhlIGRvdWJsZXRzCgpgYGB7cn0KCnNldS5kIDwtIHNldS5kWywgc2V1LmRAbWV0YS5kYXRhWywgREYubmFtZV09PSAiU2luZ2xldCJdCmRpbShzZXUuZCkKZGltKHNldSkKCiMgcmVtb3ZlZCBhYm91dCAxMDAgY2VsbHMKCgpgYGAKCgpSdW4gY2x1c3RlcmluZwoKYGBge3J9CgpzZXUucSA8LSBGaW5kTmVpZ2hib3JzKHNldS5kLCBkaW1zID0gMToyNSwgay5wYXJhbSA9IDQzKQpzZXUucSA8LSBGaW5kQ2x1c3RlcnMoc2V1LnEsIHJlc29sdXRpb24gPSBjKDAsMC4yLDAuNCwwLjYpKQpzZXUucSA8LSBGaW5kQ2x1c3RlcnMoc2V1LnEsIHJlc29sdXRpb24gPSBjKDEuMikpCgpsaWJyYXJ5KGNsdXN0cmVlKQpjbHVzdHJlZShzZXUucSwgcHJlZml4ID0gIlJOQV9zbm5fcmVzLiIpCkRpbVBsb3Qoc2V1LnEpCgpgYGAKCkxvb2sgYXQgdGhlIHByZWRpY3Rpb25zIG9mIGNlbGwgdHlwZXMgaW4gc2V1cmF0IGxhYmVsIHRyYW5zZmVyCgozIG9yZ2Fub2lkIGRhdGFzZXRzCgoKYGBge3J9CgoKIyBTTkNBIGFuZCBjb250cm9sIG1pZGJyYWluIG9yZ2Fub2lkcyAxNjUgZGF5cyBpbiBjdWx0dXJlCk1CTyA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9BU1QyM19CcmFpbkNvbW0vTUJPY2x1c3RlcnNfbmFtZXMyOTA3MjAyMS5yZHMiKQoKIyBNaWRicmFpbiAgQUlXMDAyIDEyMCBkYXlzIGluIGN1bHR1cmUKQUlXTUJPIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FJV3RyaW8xMjBkYXlzL01PaW50ZWdyYXRlZENsdXN0ZXJLMTIzcmVzMC44Lm5hbWVzX25vdjE2XzIwMjEiKQoKIyBNaWRicmFpbiBBSVcwMDIgNjAgZGF5cyBpbiBjdWx0dXJlCgpBSVc2MCA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9BSVd0cmlvNjBkYXlzL0FXSTAwMlBhcmtpbktPUGlua0tPNjBkYXlzX2xhYmVsc18xNDA1MjAyMi5yZHMiKQoKCiMgcXVlcnkKI3NldS5xIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9OZXVyb25zRmlsdGVyZWRTZXUyODA5MjAyMi5SRFMiKQoKCiNmaXJzdCBwcmVkaWN0IHdpdGggdGhlIE1CTyBkYXRhCklkZW50cyhNQk8pIDwtICJjbHVzdGVyX2xhYmVscyIKRGVmYXVsdEFzc2F5KE1CTykgPC0gIlJOQSIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKcHJpbnQoImZpbmRpbmcgcmVmZXJlbmNlIGFuY2hvcnMiKQphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gTUJPICxxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IE1CTyRjbHVzdGVyX2xhYmVscykKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCiMgYWRkIG5ldyBkYXRhc2xvdCBmb3IgTUJPIHByZWRpY3RlZCBJRCB0byBtYWtlIHRoZSBuZXh0IHByZWRpY3Rpb24Kc2V1LnEkTUJPQVNUMjMucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ01CT0FTVDIzLnByZWQnLCBsYWJlbCA9IFRSVUUpCiAKIyMgY2hlY2sgdGhlIHByb3BvcnRpb24gb2YgY2VsbCB0eXBlcyBwcmVkaWN0ZWQgaW4gZWFjaCBjbHVzdGVyCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKQpwci50LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHByb3AudGFibGUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyBjbHVzdGVycyBkb24ndCBicmVhayB1cCBieSB0aGUgcHJlZGljdGVkIGNlbGwgdHlwZXMKCiMjIyMjIyMjIyMjIyBhbm90aGVyIHByZWRpY3Rpb25zIG5vdyB1c2luZyB0aGUgQUlXIG9yZ2Fub2lkcwoKSWRlbnRzKEFJV01CTykgPC0gInJlczA4bmFtZXMiCkRlZmF1bHRBc3NheShBSVdNQk8pIDwtICJSTkEiCgphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gQUlXTUJPICxxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IEFJV01CTyRyZXMwOG5hbWVzKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRNQk9BSVcucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ01CT0FJVy5wcmVkJywgbGFiZWwgPSBUUlVFKQogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkcHJlZGljdGVkLmlkKSkKcHIudC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkcHJlZGljdGVkLmlkKSkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCgoKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgdGhlIHByZWRpY3RlZCBjZWxsIHR5cGVzIG1ha2UgbW9yZSBzZW5zZSBmcm9tIHRoZSBBSVcwMDIgb3JnYW5vaWQKIyBub3cgcHJlZGljdCB3aXRoIHRoZSBBSVcwMDIgNjAgZGF5cyBvcmdhbm9pZAoKSWRlbnRzKEFJVzYwKSA8LSAiY2x1c3Rlci5pZHMiCkRlZmF1bHRBc3NheShBSVc2MCkgPC0gIlJOQSIKCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBBSVc2MCwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBBSVc2MCRjbHVzdGVyLmlkcykgCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJEFJVzYwLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdBSVc2MC5wcmVkJywgbGFiZWwgPSBUUlVFKQogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkcHJlZGljdGVkLmlkKSkKcHIudC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkcHJlZGljdGVkLmlkKSkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyBzYXZlIG9qYmVjdCB3aXRoIHByZWRpY2l0b25zCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvTmV1cm9uczJQcmVkaWN0aW9uc1NldTMwMDkyMDIyLlJEUyIpCgoKCmBgYAoKCkxvb2sgYXQgdGhlIGNlbGwgdHlwZSBwcmVkaWN0aW9ucyBpbiBlYWNoIGNsdXN0ZXIgLSBOZXVyb25zIDEKCmBgYHtyfQojIEFJVzAwMiAxNjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuNiwgc2V1LnEkTUJPQUlXLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDEsIEZyZXEpKQoKIyBBSVcwMDIgMTYwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjYsIHNldS5xJEFJVzYwLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAgPC1hcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigxLCBGcmVxKSkKCiMgQVNUMjMgNjUgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuNiwgc2V1LnEkTUJPQVNUMjMucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigxLCBGcmVxKSkKCgpwcmVkLnRhYmxlIDwtIG1lcmdlKHRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCx0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCwgYnkgPSAnVmFyMScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMsIGJ5ID0gJ1ZhcjEnKQpwcmVkLnRhYmxlCgpgYGAKCkJhc2VkIG9uIHRoZSAzIGRpZmZlcmVudCBwcmVkaWN0aW9ucyBJIGNhbiBsYWJsZSB0aGUgY2VsbCB0eXBlcwoKMCAtIE5QQyBvciBlYXJseSBuZXVyb25zCjEgLSBpbW1hdHVyZSBleGNpdGF0b3J5IG5ldXJvbnMKMiAtIE5QQyBvciBlYXJseSBuZXVyb25zCjMgLSBSRyBvciBPbGlnb3MKNC0gRG9wYW1pbmVyZ2ljIG5ldXJvbnMgLSBwb3NzaWJseSBlYXJseQo1IC0gTlBDIG9yIGVhcmx5IG5ldXJvbnMKNiAtIFJhZGlhbCBHTGlhCgpQcmVkaWN0aW9ucyB3aXRoIHRoZSBEQSBzdWJ0eXBlcyBmcm9tIAoKYGBge3J9CiMgcmVhZCBpbiB0aGUgCnNldS5xIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9OZXVyb24xTGFibGVkU2V1MzAwOTIwMjIuUkRTIikKCkRpbVBsb3Qoc2V1LnEpCgpgYGAKClVzZSB0aGUgQmhhZHVyaSBkYXRhc2V0CgpgYGB7cn0KCiMgcmVhZCBpbiB0aGUgcmVmZXJlbmNlIGRhdGFzZXQKIyBmcm9tIEJoYWR1cmkgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCnNldS5yIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1B1YmxpY0RhdGEvQmhhZHVyaV93aG9sZUJyYWluL0JoYWR1cmlfbWlkYnJhaW5fc3RyaWF0dW0uUkRTIikKdGFibGUoc2V1LnIkY2VsbF90eXBlKQp0YWJsZShzZXUuciRjZWxsX2NsYXNzKQp0YWJsZShzZXUuciRjZWxsX2NsdXN0ZXIpCgpJZGVudHMoc2V1LnIpIDwtICJjZWxsX2NsdXN0ZXIiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRjZWxsX2NsdXN0ZXIpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRCaGEubWlkLnN0cmkucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJEJoYS5taWQuc3RyaS5wcmVkKSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQmhhLm1pZC5zdHJpLnByZWQnKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdzdWJncm91cHMnKQoKIyBkbyB0aGUgcHJlZGljdGlvbnMgZGlmZmVyIHdpdGggdGhlIG1haW4gY2VsbCB0eXBlIGdyb3VwcyBpbnN0ZWFkIG9mIHRoZSBjbHVzdGVyIGluIHRoZSByZWZlcmVuY2UgZGF0YT8gCklkZW50cyhzZXUucikgPC0gImNlbGxfdHlwZSIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJGNlbGxfdHlwZSkKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ3ByZWRpY3RlZC5pZCcpCgojIGdvb2QgbGFyZ2VzdCBwcmVkaWN0aW9uIGlzIG5ldXJvbnMuIAoKCgpgYGAKCgoKCgpJIHdpbGwgYWxzbyBmaW5kIG1hcmtlcnMgYW5kIGxvb2sgYXQgYSBsaXN0IG9mIG5ldXJvbmFsIG1hcmtlcnMKCmBgYHtyfQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC42JwpDbHVzdGVyTWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhzZXUucSwgb25seS5wb3MgPSBUUlVFKQoKdG9wNSA8LSBDbHVzdGVyTWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKG49NSwgd3QgPSBhdmdfbG9nMkZDKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gdG9wNSRnZW5lLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCgp3cml0ZS5jc3YoQ2x1c3Rlck1hcmtlcnMsIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvTmV1cm9uczFDbHVzdGVyTWFya2VyczcuY3N2IikKCgpgYGAKCkV4cGxvcmUgc29tZSBHZW5lIGV4cHJlc3Npb24gbGV2ZWxzCgpgYGB7cn0KZmVhdHVyZV9saXN0ID0gYygiTUtJNjciLCJTT1gyIiwiUE9VNUYxIiwiRExYMiIsIlBBWDYiLCJTT1g5IiwiSEVTMSIsIk5FUyIsIlJCRk9YMyIsIk1BUDIiLCJOQ0FNMSIsIkNEMjQiLCJHUklBMiIsIkdSSU4yQiIsIkdBQkJSMSIsIkdBRDEiLCJHQUQyIiwiR0FCUkExIiwiR0FCUkIyIiwiVEgiLCJBTERIMUExIiwiTE1YMUIiLCJOUjRBMiIsIkNPUklOIiwiQ0FMQjEiLCJLQ05KNiIsIkNYQ1I0IiwiSVRHQTYiLCJTTEMxQTMiLCJDRDQ0IiwiQVFQNCIsIlMxMDBCIiwgIlBER0ZSQSIsIk9MSUcyIiwiTUJQIiwiQ0xETjExIiwiVklNIiwiVkNBTTEiKQoKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCkgK1JvdGF0ZWRBeGlzKCkKClBEX3BvdWxpbiA9IGMoIlRIIiwiU0xDNkEzIiwiU0xDMThBMiIsIlNPWDYiLCJORE5GIiwiU05DRyIsIkFMREgxQTEiLCJDQUxCMSIsIlRBQ1IyIiwiU0xDMTdBNiIsIlNMQzMyQTEiLCJPVFgyIiwiR1JQIiwiTFBMIiwiQ0NLIiwiVklQIikKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBQRF9wb3VsaW4sIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBQRF9wb3VsaW4pK1JvdGF0ZWRBeGlzKCkKCmVhbHJ5TmV1ciA9IGMoIkRDWCIsIk5FVVJPRDEiLCJUQlIxIikKcHJvbGlmZXJhdGlvbiA9IGMoIlBDTkEiLCJNS0k2NyIpCm5ldXJhbHN0ZW0gPSBjKCJTT1gyIiwiTkVTIiwiUEFYNiIsIk1BU0gxIikKCmZlYXR1cmVfbGlzdCA8LSBjKCJEQ1giLCJORVVST0QxIiwiVEJSMSIsIlBDTkEiLCJNS0k2NyIsIlNPWDIiLCJORVMiLCJQQVg2IiwiTUFTSDEiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0KStSb3RhdGVkQXhpcygpCiMgbm8gcHJvbGlmZXJhdGlvbiBtYXJrZXIgZXhwcmVzc2lvbiAgUENOQSBvciBNS0k2NwojIGNsdXN0ZXIgNCBEQSBuZXVyb25zIC0gc2hvd3MgZWFybHkgbmV1cm9uIG1hcmtlciBhbmQgbG93IFBBWCA0CiMgY2x1c3RlciAzIGhhcyBoaWdoZXIgU09YMiAtIG5ldXJvYmxhc3QgbWFya2VyIC8gTlBDIG1hcmtlcgoKbWF0X25ldXJvbiA9IGMoIlJCRk9YMyIsIlNZUCIsIkRMRzQ1IiwiVkFNUDEiLCJWQU1QMiIsIlRVQkIzIiwiU1lUMSIsIkJTTiIsIkhPTUVSMSIsIlNMQzE3QTYiKSAKIyBOZXVOIGlzIEZPWDMgLSBSQkZPWDMKIyBQU0Q5NSBhbHNvIFNQLTkwIG9yIERMRzQKIyBWR0xVVDIgaXMgU0xDMTdBNgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gbWF0X25ldXJvbiwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQojIGNsdXN0ZXIgNCBhbHNvIHNob3cgbWF0dXJlIG5ldXJvbiBtYXJrZXJzCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gbWF0X25ldXJvbikrUm90YXRlZEF4aXMoKQojIGV4Y2l0YXRvcnkgbmV1cm9uIG1hcmtlcnMKZXggPSBjKCJHUklBMiIsIkdSSUExIiwiR1JJQTQiLCJHUklOMSIsIkdSSU4yQiIsIkdSSU4yQSIsIkdSSU4zQSIsIkdSSU4zIiwiR1JJUDEiLCJDQU1LMkEiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZXgsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBleCkrUm90YXRlZEF4aXMoKQojIGluaGliaXRvcnkgbmV1cm9uIG1hcmtlcnMKaW5oID0gYygiR0FEMSIsIkdBRDIiLCAiR0FUMSIsIlBWQUxCIiwiR0FCUjIiLCJHQUJSMSIsIkdCUlIxIiwiR0FCUkIyIiwiR0FCUkIxIiwiR0FCUkIzIiwiR0FCUkE2IiwiR0FCUkExIiwiR0FCUkE0IiwiVFJBSzIiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gaW5oLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gaW5oKStSb3RhdGVkQXhpcygpCiMgY2x1c3RlciA0IGlzIG1vcmUgZXhjaXRhdG9yeSB0aGFuIGluaGJpdG9yeSBidXQgbmVpdGhlciBtYXJrZXIgc2V0IGhhcyBtdWNoIGV4cHJlc3Npb24gCgoKCmBgYAoKCkNoZWNrb3V0IHRoZSBFbnJpY2hlciBjZWxsIHR5cGUgbGlicmFyaWVzIGZyb20gCgpgYGB7cn0KIyB0ZXN0IG1hcmtlcnMgZm9yIHRoZSA3IGNsdXN0ZXJzIGluIE5ldXJvbnMxIAoKbGlicmFyeShkZXZ0b29scykKaW5zdGFsbF9naXRodWIoIndqYXdhaWQvZW5yaWNoUiIpCmxpYnJhcnkoZW5yaWNoUikKCgpzZXRFbnJpY2hyU2l0ZSgiRW5yaWNociIpICMgSHVtYW4gZ2VuZXMKIyBsaXN0IG9mIGFsbCB0aGUgZGF0YWJhc2VzCgpkYnMgPC0gbGlzdEVucmljaHJEYnMoKQpkYnMKIyBsaWJhcmllcyB3aXRoIGNlbGwgdHlwZXMKCmRiIDwtIGMoJ0FsbGVuX0JyYWluX0F0bGFzX3VwJywnRGVzY2FydGVzX0NlbGxfVHlwZXNfYW5kX1Rpc3N1ZV8yMDIxJywKICAgICAgICAnQ2VsbE1hcmtlcl9BdWdtZW50ZWRfMjAyMScsJ0F6aW11dGhfQ2VsbF9UeXBlc18yMDIxJykKCiMgZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gTlVMTCkKCk4xLmMwIDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAwICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmMwJGdlbmUKCk4xLmMwLkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jMC5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmMwLkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzAuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKIyBjbHVzdGVyIDAgY291bGQgYmUgaHlwb3RoYWxtdXMsIERBIG5ldXJvbnMgQTEzCgpOMS5jMSA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMSAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jMSRnZW5lCgpOMS5jMS5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jMS5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMS5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMS5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMS5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzEuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jMS5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmMxLkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzEuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBjbHVzdGVyIDE7IG9sZmFjdG9yeSBidWxiLCBuZXVyYWwgcGxhdGUsIG1heWJlIFJhZGlhbCBHbGlhLCAKTjEuYzIgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDIgJiBhdmdfbG9nMkZDID4gMCkKZ2VuZXMgPC0gTjEuYzIkZ2VuZQoKTjEuYzIuRXIgPC0gZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gZGIpCnBsb3RFbnJpY2goTjEuYzIuRXJbWzFdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzIuRXJbWzJdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzIuRXJbWzNdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzIuRXJbWzRdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmMyLkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzIuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jMi5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgpOMS5Fci5nZW5lcy40IDwtIE4xLmMyLkVyW1s0XV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjQKCiMgY2x1c3RlciAyIHNvbWUgYnJhaW4gbnVjbGV1cywgbmV1cmFsIHN0ZW0KCk4xLmMzIDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAzICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmMzJGdlbmUKCk4xLmMzLkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmMzLkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMzLkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMzLkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMzLkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jMy5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmMzLkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzMuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jMy5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgojIGNsdXN0ZXIgMyBzdHJvbWFsIGNlbGwgb2YgdGh5bXVzLCBlbWJyeW9uaWMgYXN0cm9jeXRlcywgT1BDLCBOSyBjZWxscywgbW9ub2N5dGVzCgpOMS5jNCA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gNCAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jNCRnZW5lCgpOMS5jNC5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jNC5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNC5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNC5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNC5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzQuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jNC5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmM0LkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzQuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBEZW50YXRlIGd5cnVzIC0gZGlmZmVyZW50IGNvcnRpY2FsIGxheWVycywgbmV1cm9ucywgbmV1cm9ucywgTlBDLCBuZXVyb25zIEdBQkEsR0xVVAoKTjEuYzUgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDUgJiBhdmdfbG9nMkZDID4gMCkKZ2VuZXMgPC0gTjEuYzUkZ2VuZQoKTjEuYzUuRXIgPC0gZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gZGIpCnBsb3RFbnJpY2goTjEuYzUuRXJbWzFdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzUuRXJbWzJdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzUuRXJbWzNdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzUuRXJbWzRdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmM1LkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzUuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jNS5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgpOMS5Fci5nZW5lcy40IDwtIE4xLmM1LkVyW1s0XV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjQKCiMgY2x1c3RlciA1IGhpcHBvY2FtcHVzLCBlbmRvdGhlbGlhbCBjZWxscywgcGVyaWN5dGVzCgpOMS5jNiA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gNiAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jNiRnZW5lCgpOMS5jNi5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jNi5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzYuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jNi5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmM2LkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzYuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBjbHVzdGVyIDYgYnJhaW4gY29ydGV4LCBzaHdhbm4gY2VsbCwgZW5kb3RoZWxpYWwsIHBlcmljeXRlLCBHQUJBCgoKYGBgCgoKQWZ0ZXIgcHJlZGljdGluZyB3aXRoIHRoZSBicmFpbiBkYXRhIEkgdGhpbmsgdXNpbmcgYSBoaWdoZXIgY2x1c3RlciBudW1iZXIgd2lsbCB3b3JrIGJldHRlcgoKCmBgYHtyfQoKIyByZXMxLjIgaGFzIDEwIGNsdXN0ZXJzCgoKIyBBSVcwMDIgMTIwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4xLjIsIHNldS5xJE1CT0FJVy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzEyMCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKSA8LSBOVUxMCmRmLnRvcC5haXcxMjAkSSA8LSByb3cubmFtZXMoZGYudG9wLmFpdzEyMCkKCiMgQUlXMDAyIDYwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4xLjIsIHNldS5xJEFJVzYwLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAgPC1hcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzYwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXc2MCkgPC0gTlVMTAojIHNvbWV0aGluZyB3ZW50IHdyb25nIGFuZCB0aGVyZSBhcmUgdG9vIG1hbnkgY2x1c3RlciAwIHByZWRpY3Rpb25zCmRmLnRvcC5haXc2MCA8LSBkZi50b3AuYWl3NjAgJT4lICBmaWx0ZXIoIXJvd19udW1iZXIoKSAlaW4lIGMoMiwgMywgNCkpCmRmLnRvcC5haXc2MCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3NjApCgojIEFTVDIzIDE2NSBkYXlzIHByZWRpY3Rpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMS4yLCBzZXUucSRNQk9BU1QyMy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuQVNUMjMgPC0gdG9wLnByZWQuY2VsbHR5cGUuQVNUMjNbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQVNUMjMkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLkFTVDIzKSA8LSBOVUxMCmRmLnRvcC5BU1QyMyRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQVNUMjMpCmRmLnRvcC5BU1QyMyA8LSBkZi50b3AuYWl3NjAgJT4lICBmaWx0ZXIoIXJvd19udW1iZXIoKSAlaW4lIGMoMjIpKQoKIyMjIGFkZCBpbiB0aGUgcHJlZGljdGlvbiBmcm9tIGJyYWluIGRhdGEgQmhhZHVyaSBtaWRicmFpbiBhbmQgc3RyaWF0dW0KdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4xLjIsIHNldS5xJEJoYS5taWQuc3RyaS5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkJoYSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkJoYSA8LSB0b3AucHJlZC5jZWxsdHlwZS5CaGFbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQmhhJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkJoYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQmhhKSA8LSBOVUxMCmRmLnRvcC5CaGEkSSA8LSByb3cubmFtZXMoZGYudG9wLkJoYSkKCgpwcmVkLnRhYmxlIDwtIG1lcmdlKGRmLnRvcC5BU1QyMywgZGYudG9wLmFpdzYwLCBieSA9ICdJJywgYWxsID0gVFJVRSkKcHJlZC50YWJsZSA8LSBtZXJnZShwcmVkLnRhYmxlLCBkZi50b3AuYWl3MTIwLCBieSA9ICdJJykKcHJlZC50YWJsZSA8LSBtZXJnZShwcmVkLnRhYmxlLCBkZi50b3AuQmhhLCBieSA9ICdJJykKcHJlZC50YWJsZQoKIyBjbHVzdGVyIDMgaXMgcHJlZGljdGVkIGFzIE9saWdvLCBSRywgYXN0cm8KCmBgYAoKCgoKTGlicmFyeSBvZiB0aXNzdWUgY2VsbCB0eXBlcyBmb3IgdXAgcmVndWxhdGVkIGdlbmVzIHBlciBjbHVzdGVyCjAgLSBoeXBvdGhhbG11cywgREEgQTEzCjEtIG5ldXJhbCBwbGF0ZSwgUmFkaWFsIEdsaWEKMiAtIE5ldXJhbCBzdGVtCjMgLSBzdHJvbWFsLCBhc3RybyBPUEMKNCAtIE5ldXJvbnMKNSAtIGVuZG90aGVsaWFsLCBwZXJpY3l0ZQo2IC0gbWF5YmUgbmV1cm9ucyBtYXliZSBub3QKCgoKCgoKQnkgdGhlIGNvbWJpbmVkIGluZm9ybWF0aW9uIC0gYW5ub3RhdGUgdGhlIGNsdXN0ZXJzIGluIE5ldXJvbnMxCgpgYGB7cn0KI0Jhc2VkIG9uIHRoZSAzIGRpZmZlcmVudCBwcmVkaWN0aW9ucyBJIGNhbiBsYWJsZSB0aGUgY2VsbCB0eXBlcwoKIzAgLSBOUEMgb3IgZWFybHkgbmV1cm9ucwojMSAtIGltbWF0dXJlIGV4Y2l0YXRvcnkgbmV1cm9ucwojMiAtIE5QQyBvciBlYXJseSBuZXVyb25zCiMzIC0gUkcgb3IgT2xpZ29zCiM0LSBEb3BhbWluZXJnaWMgbmV1cm9ucyAtIHBvc3NpYmx5IGVhcmx5CiM1IC0gTlBDIG9yIGVhcmx5IG5ldXJvbnMKIzYgLSBSYWRpYWwgR2xpYQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC42JwpjbHVzdGVyLmlkcyA8LSBjKCJJbW1hdHVyZU5ldXJvbnMiLCJOZXVyb25zIiwiTlBDIiwiT1BDLVJHIiwiREFuZXVyb25zIiwKICAgICAgICAgICAgICAgICAiT3RoZXIiLCJSRyIpCnVuaXF1ZShzZXUucSRSTkFfc25uX3Jlcy4wLjYpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRzdWJncm91cHMgPC0gSWRlbnRzKHNldS5xKQoKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycsIHJlcGVsID0gVFJVRSkKCgpzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL05ldXJvbjFMYWJsZWRTZXUzMDA5MjAyMi5SRFMiKQoKCgpgYGAKCmBgYHtyfQpsaWJyYXJ5KGNsdXN0cmVlKQpjbHVzdHJlZShzZXUucSkKCmBgYAoKCgoKQWZ0ZXIgcHJlZGljdGluZyB3aXRoIHRoZSBicmFpbiBkYXRhIEJoYWR1cmkgTWlkYnJhaW4gYW5kIFN0cmlhdHVtIC0gY2hvb3NlIG1haW4gY2VsbCB0eXBlcyB0byBsYWJlbApUcmllZCB0aGUgcHJlZGljdGlvbnMgd2l0aCBtb3JlIGNsdXN0ZXJzIDAtOSByZXMgMS4yCgpgYGB7cn0KCgojMCAtIE5QQyBvciBlYXJseSBuZXVyb25zICAgICAgICAgID0gMCw3LDIKIzEgLSBpbW1hdHVyZSBleGNpdGF0b3J5IG5ldXJvbnMgPSAxLDgKIzIgLSBOUEMgb3IgZWFybHkgbmV1cm9ucyAgICAgICAgID0gNCw2CiMzIC0gUkcgb3IgT2xpZ29zICAgICAgICAgICAgICAgICA5LDIsNwojNC0gRG9wYW1pbmVyZ2ljIG5ldXJvbnMgLSBwb3NzaWJseSBlYXJseSAgID0gMwojNSAtIE5QQyBvciBlYXJseSBuZXVyb25zICAgPSA1CiM2IC0gUmFkaWFsIEdsaWEgICA9IDkgCgoKIyAwIC0gbmV1cm9ucyAoTlBDKQojIDEgLSBuZXVyb25zIChOUEMpCiMgMiAtIGFzdHJvIChOUEMpCiMgMyAtIG5ldXJvbnMgKERBKQojIDQgLSBuZXVyb25zIChOUEMpCiMgNSAtIEVuZG90aGVsaWFsIChwcmVkaWN0ZWQgZnJvbSBCaGEsIHByZWRpY3RlZCBhcyBOUEMpCiMgNiAtIE5ldXJvbnMvIEdsaWEKIyA3IC0gQXN0cm9jeXRlcyBSRwojIDggLSBOZXVyb25zCiMgOSAtIEVuZG90aGVsaWFsIFJHLCBuZXVyb25zCgpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4xLjInCmNsdXN0ZXIuaWRzIDwtIGMoIk5ldXJvbnMiLCJOZXVyb25zIiwiTmV1cm9ucyIsIk5ldXJvbnMiLCJOZXVyb25zIiwKICAgICAgICAgICAgICAgICAiRW5kb3RoZWxpYWwiLCJOZXVyb25zIiwiR2xpYSIsIk5ldXJvbnMiLCJSYWRpYWwgR2xpYSIpCnVuaXF1ZShzZXUucSRSTkFfc25uX3Jlcy4wLjYpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRDZWxsX1R5cGVzIDwtIElkZW50cyhzZXUucSkKCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdDZWxsX1R5cGVzJywgcmVwZWwgPSBUUlVFKQoKc2F2ZVJEUyhzZXUucSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9OZXVyb24xTGFibGVkU2V1MzAwOTIwMjIuUkRTIikKCgpgYGAKCgoKCgoKCgojIyMgTmV4dCBSZXBlYXQgZXZlcnl0aGluZyBmb3IgTmV1cm9uczIKCmBgYHtyfQojIGV4cGxvcmUgZmlsdGVyaW5nCnNldSA8LSBOZXVyb25zMgpzZXUKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSwgbmNvbCA9IDMpCgpWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiksIHkubWF4ID0gMjAwMCkKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIpLCB5Lm1heCA9IDM1MCkKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5Db3VudF9STkEiKSwgeS5tYXggPSAyMDAwKQoKIyBmaWx0ZXIgbW9yZSBjZWxscwoKc2V1LmZ0IDwtIHN1YnNldChzZXUsIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDMwMCAmIG5Db3VudF9STkEgPiA1MDAgJiBuQ291bnRfUk5BIDwgMTAwMDApIApzZXUuZnQKCiMgMTc2MDQgc2FtcGxlcyB3aXRoIDI1MCBuRmVhdHVyZV9STkEKIyA5NjU3IHdpdGggIG5GZWF0dXJlIDMwMCBhbmQgbkNPdW50IDUwMAoKCmBgYAoKCkRvdWJsZXQgZmluZGVyIAoKYGBge3J9CnN1cHByZXNzTWVzc2FnZXMocmVxdWlyZShEb3VibGV0RmluZGVyKSkKCiMgZmlsdGVyaW5nIG91dCBNQUxBVDEgYW5kIG1pdG9jaG9uZHJpYWwgZ2VuZXMKCnNldS5mdCA8LSBzZXUuZnRbIWdyZXBsKCJNQUxBVDEiLCByb3duYW1lcyhzZXUpKSwgXQpzZXUuZnQgPC0gc2V1LmZ0WyFncmVwbCgiXk1ULSIsIHJvd25hbWVzKHNldS5mdCkpLCBdCgojIGxpa2UgaW4gdGhlIHR1dG9yaWFsIEknbSBmb2xsb3dpbmcgTUFMQVQxIGlzIHRoZSB0b3AgbW9zdCBleHByZXNzZWQgZ2VuZS4gIFRoZSB0b3AgZ2VuZXMgYXJlIGEgbG90IG9mIE1UIGFuZCBSaWJvc29tYWwgZ2VuZXMKCnNldS5mdFtbInBlcmNlbnQucmIiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoc2V1LmZ0LCBwYXR0ZXJuID0gIl5SUCIpCgpzZXUuZCA9IE5vcm1hbGl6ZURhdGEoc2V1LmZ0KQpzZXUuZCA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldS5kLCB2ZXJib3NlID0gRikKc2V1LmQgPSBTY2FsZURhdGEoc2V1LmQsIHZhcnMudG8ucmVncmVzcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJwZXJjZW50Lm10IiksCiAgICB2ZXJib3NlID0gRikKc2V1LmQgPSBSdW5QQ0Eoc2V1LmQsIHZlcmJvc2UgPSBGLCBucGNzID0gMzApCnNldS5kID0gUnVuVU1BUChzZXUuZCwgZGltcyA9IDE6MTAsIHZlcmJvc2UgPSBGKQoKbkV4cCA8LSByb3VuZChuY29sKHNldS5kKSAqIDAuMDgpICAjIGV4cGVjdCBtb3JlIGRvdWJsZXRzIGJlY2F1c2UgdGhlcmUgaXMgYSBsb3QgbW9yZSBjZWxscwpzZXUuZCA8LSBkb3VibGV0RmluZGVyX3YzKHNldS5kLCBwTiA9IDAuMjUsIHBLID0gMC4wOSwgbkV4cCA9IG5FeHAsIFBDcyA9IDE6MTApCgoKIyBuYW1lIG9mIHRoZSBERiBwcmVkaWN0aW9uIGNhbiBjaGFuZ2UsIHNvIGV4dHJhY3QgdGhlIGNvcnJlY3QgY29sdW1uIG5hbWUuCkRGLm5hbWUgPSBjb2xuYW1lcyhzZXUuZEBtZXRhLmRhdGEpW2dyZXBsKCJERi5jbGFzc2lmaWNhdGlvbiIsIGNvbG5hbWVzKHNldS5kQG1ldGEuZGF0YSkpXQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChuY29sID0gMiwgRGltUGxvdChzZXUuZCwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpICsgTm9BeGVzKCksCiAgICBEaW1QbG90KHNldS5kLCBncm91cC5ieSA9IERGLm5hbWUpICsgTm9BeGVzKCkpCgpWbG5QbG90KHNldS5kLCBmZWF0dXJlcyA9ICJuRmVhdHVyZV9STkEiLCBncm91cC5ieSA9IERGLm5hbWUsIHB0LnNpemUgPSAwLjEpCgpgYGAKCgpSZW1vdmUgdGhlIGRvdWJsZXQgY2VsbHMKCmBgYHtyfQoKc2V1LmQgPC0gc2V1LmRbLCBzZXUuZEBtZXRhLmRhdGFbLCBERi5uYW1lXT09ICJTaW5nbGV0Il0KZGltKHNldS5kKQpkaW0oc2V1KQojIDk2NTcgY2VsbHMgcHJlIGZpbHRlcgojIDg4ODQgY2VsbHMgYWZ0ZXIgZmlsdGVyaW5nCiMgbm90ZSB0aGUgcGVyY2VudCBkb3VibGVzIGV4cGVjdGVkIGlzIGNsb3NlIHRvIHRoZSBwZXJjZW50IGRldGVjdGVkCgpgYGAKCgpSZXBlYXQgd29ya2Zsb3cgd2l0aCBkb3VibGV0IHJlbW92ZWQgZGF0YSBhbmQgZmluZCBjbHVzdGVycyBmb3IgCgpgYGB7cn0KCgpzZXUgPC0gTm9ybWFsaXplRGF0YShzZXUuZCwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCnNldSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzZXUsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgbmZlYXR1cmVzID0gMjAwMCkKc2V1IDwtIFNjYWxlRGF0YShzZXUpCnNldSA8LSBSdW5QQ0Eoc2V1KQpzZXUgPC0gUnVuVU1BUChzZXUsIHJlZHVjdGlvbiA9ICJwY2EiLCBuLm5laWdoYm9ycyA9IDQzLCBkaW1zID0gMTozMCkKRGltUGxvdChzZXUsIHJlZHVjdGlvbiA9ICJ1bWFwIikKCnNldS5xIDwtIEZpbmROZWlnaGJvcnMoc2V1LCBkaW1zID0gMToyNSwgay5wYXJhbSA9IDQzKQpzZXUucSA8LSBGaW5kQ2x1c3RlcnMoc2V1LnEsIHJlc29sdXRpb24gPSBjKDAsMC4yLDAuNCwwLjYpKQoKbGlicmFyeShjbHVzdHJlZSkKY2x1c3RyZWUoc2V1LnEpCgpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuMicpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC40JykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjEuMicpCgpgYGAKCkxhYmVsIGNlbGwgdHlwZXMgdXNpbmcgdGhlIGxhYmVsIHRyYW5zZmVyCgpgYGB7cn0KCgoKIyBTTkNBIGFuZCBjb250cm9sIG1pZGJyYWluIG9yZ2Fub2lkcyAxNjUgZGF5cyBpbiBjdWx0dXJlCk1CTyA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9BU1QyM19CcmFpbkNvbW0vTUJPY2x1c3RlcnNfbmFtZXMyOTA3MjAyMS5yZHMiKQoKIyBNaWRicmFpbiAgQUlXMDAyIDEyMCBkYXlzIGluIGN1bHR1cmUKQUlXTUJPIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FJV3RyaW8xMjBkYXlzL01PaW50ZWdyYXRlZENsdXN0ZXJLMTIzcmVzMC44Lm5hbWVzX25vdjE2XzIwMjEiKQoKIyBNaWRicmFpbiBBSVcwMDIgNjAgZGF5cyBpbiBjdWx0dXJlCgpBSVc2MCA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9BSVd0cmlvNjBkYXlzL0FXSTAwMlBhcmtpbktPUGlua0tPNjBkYXlzX2xhYmVsc18xNDA1MjAyMi5yZHMiKQoKCiMgcXVlcnkKI3NldS5xIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9OZXVyb25zRmlsdGVyZWRTZXUyODA5MjAyMi5SRFMiKQoKCiNmaXJzdCBwcmVkaWN0IHdpdGggdGhlIE1CTyBkYXRhCklkZW50cyhNQk8pIDwtICJjbHVzdGVyX2xhYmVscyIKRGVmYXVsdEFzc2F5KE1CTykgPC0gIlJOQSIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKcHJpbnQoImZpbmRpbmcgcmVmZXJlbmNlIGFuY2hvcnMiKQphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gTUJPICxxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IE1CTyRjbHVzdGVyX2xhYmVscykKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCiMgYWRkIG5ldyBkYXRhc2xvdCBmb3IgTUJPIHByZWRpY3RlZCBJRCB0byBtYWtlIHRoZSBuZXh0IHByZWRpY3Rpb24Kc2V1LnEkTUJPQVNUMjMucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ01CT0FTVDIzLnByZWQnLCBsYWJlbCA9IFRSVUUpCiAKIyMgY2hlY2sgdGhlIHByb3BvcnRpb24gb2YgY2VsbCB0eXBlcyBwcmVkaWN0ZWQgaW4gZWFjaCBjbHVzdGVyCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKQpwci50LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHByb3AudGFibGUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyBjbHVzdGVycyBkb24ndCBicmVhayB1cCBieSB0aGUgcHJlZGljdGVkIGNlbGwgdHlwZXMKCiMjIyMjIyMjIyMjIyBhbm90aGVyIHByZWRpY3Rpb25zIG5vdyB1c2luZyB0aGUgQUlXIG9yZ2Fub2lkcwoKSWRlbnRzKEFJV01CTykgPC0gInJlczA4bmFtZXMiCkRlZmF1bHRBc3NheShBSVdNQk8pIDwtICJSTkEiCgphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gQUlXTUJPICxxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IEFJV01CTyRyZXMwOG5hbWVzKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRNQk9BSVcucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ01CT0FJVy5wcmVkJywgbGFiZWwgPSBUUlVFKQogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkcHJlZGljdGVkLmlkKSkKcHIudC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkcHJlZGljdGVkLmlkKSkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCgoKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgdGhlIHByZWRpY3RlZCBjZWxsIHR5cGVzIG1ha2UgbW9yZSBzZW5zZSBmcm9tIHRoZSBBSVcwMDIgb3JnYW5vaWQKIyBub3cgcHJlZGljdCB3aXRoIHRoZSBBSVcwMDIgNjAgZGF5cyBvcmdhbm9pZAoKSWRlbnRzKEFJVzYwKSA8LSAiY2x1c3Rlci5pZHMiCkRlZmF1bHRBc3NheShBSVc2MCkgPC0gIlJOQSIKCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBBSVc2MCwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBBSVc2MCRjbHVzdGVyLmlkcykgCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJEFJVzYwLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdBSVc2MC5wcmVkJywgbGFiZWwgPSBUUlVFKQogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkcHJlZGljdGVkLmlkKSkKcHIudC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkcHJlZGljdGVkLmlkKSkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCgoKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgc2F2ZSBvamJlY3Qgd2l0aCBwcmVkaWNpdG9ucwpzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL05ldXJvbnMyUHJlZGljdGlvbnNTZXUzMDA5MjAyMi5SRFMiKQoKCgpgYGAKCgpQcmVkaWN0IGZyb20gQnJhaW4gQmhhaGFuaQoKYGBge3J9CgpzZXUucSA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvTmV1cm9uczJMYWJlbHNTZXUzMDA5MjAyMi5SRFMiKQoKCiMgcmVhZCBpbiB0aGUgcmVmZXJlbmNlIGRhdGFzZXQKIyBmcm9tIEJoYWR1cmkgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCnNldS5yIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1B1YmxpY0RhdGEvQmhhZHVyaV93aG9sZUJyYWluL0JoYWR1cmlfbWlkYnJhaW5fc3RyaWF0dW0uUkRTIikKdGFibGUoc2V1LnIkY2VsbF90eXBlKQp0YWJsZShzZXUuciRjZWxsX2NsYXNzKQp0YWJsZShzZXUuciRjZWxsX2NsdXN0ZXIpCgpJZGVudHMoc2V1LnIpIDwtICJjZWxsX2NsdXN0ZXIiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRjZWxsX2NsdXN0ZXIpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRCaGEubWlkLnN0cmkucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJEJoYS5taWQuc3RyaS5wcmVkKSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQmhhLm1pZC5zdHJpLnByZWQnKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdzdWJncm91cHMnKQoKIyBkbyB0aGUgcHJlZGljdGlvbnMgZGlmZmVyIHdpdGggdGhlIG1haW4gY2VsbCB0eXBlIGdyb3VwcyBpbnN0ZWFkIG9mIHRoZSBjbHVzdGVyIGluIHRoZSByZWZlcmVuY2UgZGF0YT8gCklkZW50cyhzZXUucikgPC0gImNlbGxfdHlwZSIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJGNlbGxfdHlwZSkKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ3ByZWRpY3RlZC5pZCcpCgojIGdvb2QgbGFyZ2VzdCBwcmVkaWN0aW9uIGlzIG5ldXJvbnMuIAoKCmBgYAoKCgpTZWUgdGhlIHRvcCBwcmVkaWN0aW9ucyBmb3IgZWFjaCBjbHVzdGVyIGluIE5ldXJvbnMyIHJlcyAwNiAKCgpgYGB7cn0KCiMgQUlXMDAyIDEyMCBkYXlzIHByZWRpY3Rpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC42LCBzZXUucSRNQk9BSVcucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjAgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5haXcxMjAgPC0gdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BSVcxMjAkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFpdzEyMCkgPC0gTlVMTApkZi50b3AuYWl3MTIwIDwtIGRmLnRvcC5haXcxMjAgJT4lICBmaWx0ZXIoIXJvd19udW1iZXIoKSAlaW4lIGMoMjAsIDIxLCAyMiwgMjMsIDI0KSkKZGYudG9wLmFpdzEyMCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKQoKIyBBSVcwMDIgNjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuNiwgc2V1LnEkQUlXNjAucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCA8LWFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3NjAgPC0gdG9wLnByZWQuY2VsbHR5cGUuQUlXNjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFpdzYwKSA8LSBOVUxMCiMgc29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHRoZXJlIGFyZSB0b28gbWFueSBjbHVzdGVyIDAgcHJlZGljdGlvbnMKI2RmLnRvcC5haXc2MCA8LSBkZi50b3AuYWl3NjAgJT4lICBmaWx0ZXIoIXJvd19udW1iZXIoKSAlaW4lIGMoMiwgMywgNCkpCmRmLnRvcC5haXc2MCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3NjApCgojIEFTVDIzIDE2NSBkYXlzIHByZWRpY3Rpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC42LCBzZXUucSRNQk9BU1QyMy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuQVNUMjMgPC0gdG9wLnByZWQuY2VsbHR5cGUuQVNUMjNbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQVNUMjMkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLkFTVDIzKSA8LSBOVUxMCmRmLnRvcC5BU1QyMyA8LSBkZi50b3AuQVNUMjMgJT4lICBmaWx0ZXIoIXJvd19udW1iZXIoKSAlaW4lIGMoMjAsIDIxLCAyMiwgMjMsIDI0LCAyNSkpCmRmLnRvcC5BU1QyMyRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQVNUMjMpCgojIyMgYWRkIGluIHRoZSBwcmVkaWN0aW9uIGZyb20gYnJhaW4gZGF0YSBCaGFkdXJpIG1pZGJyYWluIGFuZCBzdHJpYXR1bQp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuNiwgc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQmhhIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuQmhhIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkJoYVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5CaGEkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQmhhJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5CaGEpIDwtIE5VTEwKZGYudG9wLkJoYSRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQmhhKQoKCnByZWQudGFibGUgPC0gbWVyZ2UoZGYudG9wLkFTVDIzLCBkZi50b3AuYWl3NjAsIGJ5ID0gJ0knLCBhbGwgPSBUUlVFKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5haXcxMjAsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlCgojIGNsdXN0ZXIgMyBpcyBwcmVkaWN0ZWQgYXMgT2xpZ28sIFJHLCBhc3RybwoKCgoKYGBgCgoKYGBge3J9CiMgcmVzMS4yIGhhcyAxNSBjbHVzdGVycwoKIyBBSVcwMDIgMTIwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4xLjIsIHNldS5xJE1CT0FJVy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzEyMCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKSA8LSBOVUxMCmRmLnRvcC5haXcxMjAgPC0gZGYudG9wLmFpdzEyMCAlPiUgIGZpbHRlcighcm93X251bWJlcigpICVpbiUgYygyNiwgMjcsIDI4LCAyOSwgMzApKQpkZi50b3AuYWl3MTIwJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5haXcxMjApCgojIEFJVzAwMiA2MCBkYXlzIHByZWRpY3Rpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMS4yLCBzZXUucSRBSVc2MC5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwIDwtYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5haXc2MCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVc2MFtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BSVc2MCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3NjApIDwtIE5VTEwKIyBzb21ldGhpbmcgd2VudCB3cm9uZyBhbmQgdGhlcmUgYXJlIHRvbyBtYW55IGNsdXN0ZXIgMCBwcmVkaWN0aW9ucwojZGYudG9wLmFpdzYwIDwtIGRmLnRvcC5haXc2MCAlPiUgIGZpbHRlcighcm93X251bWJlcigpICVpbiUgYygyLCAzLCA0KSkKZGYudG9wLmFpdzYwJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5haXc2MCkKCiMgQVNUMjMgMTY1IGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4xLjIsIHNldS5xJE1CT0FTVDIzLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5BU1QyMyA8LSB0b3AucHJlZC5jZWxsdHlwZS5BU1QyM1tvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQVNUMjMpIDwtIE5VTEwKZGYudG9wLkFTVDIzIDwtIGRmLnRvcC5BU1QyMyAlPiUgIGZpbHRlcighcm93X251bWJlcigpICVpbiUgYygyNiwgMjcsIDI4LCAyOSwgMzAsIDMxKSkKZGYudG9wLkFTVDIzJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykKCgojIyMgYWRkIGluIHRoZSBwcmVkaWN0aW9uIGZyb20gYnJhaW4gZGF0YSBCaGFkdXJpIG1pZGJyYWluIGFuZCBzdHJpYXR1bQp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjEuMiwgc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQmhhIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuQmhhIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkJoYVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5CaGEkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQmhhJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5CaGEpIDwtIE5VTEwKZGYudG9wLkJoYSRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQmhhKQoKCnByZWQudGFibGUgPC0gbWVyZ2UoZGYudG9wLkFTVDIzLCBkZi50b3AuYWl3NjAsIGJ5ID0gJ0knLCBhbGwgPSBUUlVFKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5haXcxMjAsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlCgojIG5ldXJvbnMgOTggbWF0Y2hlcyB1cCB3aXRoIERBIG5ldXJvbnMKCmBgYAoKCgoKV2hhdCBjZWxsIHR5cGVzIGFyZSBwcmVkaWN0ZWQgYWNyb3NzIHRoZSAzIHJlZmVyZW5jZXMKCjAgLSBOZXVyb25zIGVhcmx5ICwgTlBDLCBuZXVyb25zIGV4Y2l0YXRvcnkKMSAtIE5ldXJvbnMgZWFybHksIE5QQwoyIC0gTmV1cm9ucyBlYXJseSwgTlBDLCBuZXVyb25zIGV4Y2l0YXRvcnkgc29tZSBEQSBuZXVyb25zCjMgLSBPbGlnbywgUkcsIAo0IC0gRXhjaXRhdG9yeSBuZXVyb25zLCBOUEMsIGVhcmx5IG5ldXJvbnMKNSAtIERBIG5ldXJvbnMsIGVhcmx5IERBIG5ldXJvbnMKNiAtIG5ldXJvbnMgaW1tYXR1cmUgTlBDCjcgLSBEQSBuZXVyb25zCjggLSBSRywgb2xpZ28sIE9QQywgTlBDCjkgLSBSYWRpYWwgR2xpYQoxMCAtIE5QQywgbmV1cm9ucywgb2xpZ28KMTEgLSBOUEMsIG5ldXJvbnMsIG9saWdvCgoKRmluZCBjbHVzdGVyIG1hcmtlcnMgYW5kIHNlZSBob3cgdGhvc2Ugd291bGQgYW5ub3RhdGUKCmBgYHtyfQpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4wLjYnCkNsdXN0ZXJNYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHNldS5xLCBvbmx5LnBvcyA9IFRSVUUpCgp0b3A1IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obj01LCB3dCA9IGF2Z19sb2cyRkMpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSB0b3A1JGdlbmUsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKCndyaXRlLmNzdihDbHVzdGVyTWFya2VycywiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9OZXVyb25zMUNsdXN0ZXJNYXJrZXJzMTEuY3N2IikKCmBgYApDbHVzdGVyIDAgaGFzIGZld2VyIG1hcmtlcnMuIAoyIGFuZCA1IGhhdmUgc2ltaWxhciB1cCByZWcgbWFya2VycwozIGFuZCA0IGFsc28gb3ZlcmxhcAoKCgpMb29rIGF0IHRoZSBjbHVzdGVyIG1hcmtlcnMgaW4gY2VsbCB0eXBlIGxpYnJhcmllcyBmb3IgTmV1cm9ucyAyCgpgYGB7cn0KbGlicmFyeShlbnJpY2hSKQpkYiA8LSBjKCdBbGxlbl9CcmFpbl9BdGxhc191cCcsJ0Rlc2NhcnRlc19DZWxsX1R5cGVzX2FuZF9UaXNzdWVfMjAyMScsCiAgICAgICAgJ0NlbGxNYXJrZXJfQXVnbWVudGVkXzIwMjEnLCdBemltdXRoX0NlbGxfVHlwZXNfMjAyMScpCgojIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IE5VTEwpCiMgY2x1c3RlciAwCgpOMS5jMCA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMCAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jMCRnZW5lCgpOMS5jMC5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jMC5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMC5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMC5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzAuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jMC5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmMwLkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCiMgY2x1c3RlciAxCgpOMS5jMSA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMSAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jMSRnZW5lCgpOMS5jMS5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jMS5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMS5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMS5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMS5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzEuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jMS5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmMxLkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzEuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBjbHVzdGVyIDE7IG9sZmFjdG9yeSBidWxiLCBuZXVyYWwgcGxhdGUsIG1heWJlIFJhZGlhbCBHbGlhLCAKTjEuYzIgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDIgJiBhdmdfbG9nMkZDID4gMCkKZ2VuZXMgPC0gTjEuYzIkZ2VuZQoKTjEuYzIuRXIgPC0gZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gZGIpCnBsb3RFbnJpY2goTjEuYzIuRXJbWzFdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzIuRXJbWzJdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzIuRXJbWzNdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzIuRXJbWzRdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmMyLkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzIuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jMi5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgpOMS5Fci5nZW5lcy40IDwtIE4xLmMyLkVyW1s0XV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjQKCiMgY2x1c3RlciAzCgpOMS5jMyA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMyAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jMyRnZW5lCgpOMS5jMy5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jMy5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMy5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMy5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMy5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzMuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jMy5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmMzLkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzMuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBjbHVzdGVyIDQKCk4xLmM0IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA0ICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmM0JGdlbmUKCk4xLmM0LkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmM0LkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM0LkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM0LkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM0LkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jNC5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmM0LkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzQuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jNC5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgojIGNsdXN0ZXIgNQoKTjEuYzUgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDUgJiBhdmdfbG9nMkZDID4gMCkKZ2VuZXMgPC0gTjEuYzUkZ2VuZQoKTjEuYzUuRXIgPC0gZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gZGIpCnBsb3RFbnJpY2goTjEuYzUuRXJbWzFdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzUuRXJbWzJdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzUuRXJbWzNdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzUuRXJbWzRdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmM1LkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzUuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jNS5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgpOMS5Fci5nZW5lcy40IDwtIE4xLmM1LkVyW1s0XV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjQKCiMgY2x1c3RlciA2CgpOMS5jNiA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gNiAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jNiRnZW5lCgpOMS5jNi5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jNi5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzYuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jNi5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmM2LkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzYuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKCiMgb3RoZXIgY2x1c3RlcnMgLSBjaGFuZ2UgdGhlIGNsdXN0ZXIgbnVtYmVyCk4xLmM2IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAxMSAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jNiRnZW5lCgpOMS5jNi5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jNi5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNi5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzYuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jNi5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmM2LkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzYuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKCmBgYAoKY2x1c3RlciAwIC0gYXN0cm9jeXRlLCByYWRpYWwgZ2xpYSwgbWljcm9nbGlhLCBzdHJpYXR1bSAtIHZlcnkgZmV3IGdlbmVzIGluIHRoZXNlIHRlcm1zCmNsdXN0ZXIgMSAtIGFkcmVuYWwsIHRoYWxtdXMsIGVuZG90aGVsaWFsLCBhc3Ryb2N5dGVzLCBuZXVyb25zCmNsdXN0ZXJzIDIgLSBuZXVyYWwgcGxhdGUsIHN0cmF0dW0sIG5ldXJvbnMsIE5QQywgc3RlbSwgYXN0cm8sbmV1cm9lbmRvY3JpbmUKY2x1c3RlciAzIC0gYnJhaW4gbW9sZWN1bGFyIGxheWVyLCBlbmRvdGhlbGlhbCwgYXN0cm9jeXRlIGVtYnJ5b25pYywgCmNsdXN0ZXIgNCAtIERHLCBzdHJpYXR1bSBDQTMsIEdBQkFlcmdpYyBuZXVyb25zCmNsdXN0ZXIgNSAtIERHLCBuZXVyb25zLCBHbHV0YW1hdGVyZ2ljIG5ldXJvbnMKY2x1c3RlciA2IC0gbmV1cm9ucywgYXN0cm9jeXRlLCBtaWNyb2dsaWEsIEdBQkFuZXVyb25zCmNsdXN0ZXIgNyAtIG5ldXJvbnMsIGdsdXQgYW5kIGdhYmEKY2x1c3RlciA4IC0gYXN0cm9jeXRlLCBlbmRvdGhlbGlhbCwgcGVyaWN5dGUKY2x1c3RlciA5IC0gZXBpdGhlbGlhbCwgZW1icnlvbmljIGFzdHJvY3l0ZXMsIEdBQkEgbmV1cm9ucwpjbHVzdGVyIDEwIC0gZXBpdGhlbGlhbApjbHVzdGVyIDExIC0gZW5kb3RoZWxpYWwsIGltbXVuZSBjZWxscyBUIGNlbGxzCgoKRXhwcmVzc2lvbiBvZiBtYXJrZXJzIGdlbmVzIGluIE5ldXJvbnMyIAoKYGBge3J9CgpmZWF0dXJlX2xpc3QgPSBjKCJNS0k2NyIsIlNPWDIiLCJQT1U1RjEiLCJETFgyIiwiUEFYNiIsIlNPWDkiLCJIRVMxIiwiTkVTIiwiUkJGT1gzIiwiTUFQMiIsIk5DQU0xIiwiQ0QyNCIsIkdSSUEyIiwiR1JJTjJCIiwiR0FCQlIxIiwiR0FEMSIsIkdBRDIiLCJHQUJSQTEiLCJHQUJSQjIiLCJUSCIsIkFMREgxQTEiLCJMTVgxQiIsIk5SNEEyIiwiQ09SSU4iLCJDQUxCMSIsIktDTko2IiwiQ1hDUjQiLCJJVEdBNiIsIlNMQzFBMyIsIkNENDQiLCJBUVA0IiwiUzEwMEIiLCAiUERHRlJBIiwiT0xJRzIiLCJNQlAiLCJDTEROMTEiLCJWSU0iLCJWQ0FNMSIpCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0KSArUm90YXRlZEF4aXMoKQoKUERfcG91bGluID0gYygiVEgiLCJTTEM2QTMiLCJTTEMxOEEyIiwiU09YNiIsIk5ETkYiLCJTTkNHIiwiQUxESDFBMSIsIkNBTEIxIiwiVEFDUjIiLCJTTEMxN0E2IiwiU0xDMzJBMSIsIk9UWDIiLCJHUlAiLCJMUEwiLCJDQ0siLCJWSVAiKQoKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IFBEX3BvdWxpbiwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IFBEX3BvdWxpbikrUm90YXRlZEF4aXMoKQoKZWFscnlOZXVyID0gYygiRENYIiwiTkVVUk9EMSIsIlRCUjEiKQpwcm9saWZlcmF0aW9uID0gYygiUENOQSIsIk1LSTY3IikKbmV1cmFsc3RlbSA9IGMoIlNPWDIiLCJORVMiLCJQQVg2IiwiTUFTSDEiKQoKZmVhdHVyZV9saXN0IDwtIGMoIkRDWCIsIk5FVVJPRDEiLCJUQlIxIiwiUENOQSIsIk1LSTY3IiwiU09YMiIsIk5FUyIsIlBBWDYiLCJNQVNIMSIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QpK1JvdGF0ZWRBeGlzKCkKIyBubyBwcm9saWZlcmF0aW9uIG1hcmtlciBleHByZXNzaW9uICBQQ05BIG9yIE1LSTY3CiMgY2x1c3RlciA0IERBIG5ldXJvbnMgLSBzaG93cyBlYXJseSBuZXVyb24gbWFya2VyIGFuZCBsb3cgUEFYIDQKIyBjbHVzdGVyIDMgaGFzIGhpZ2hlciBTT1gyIC0gbmV1cm9ibGFzdCBtYXJrZXIgLyBOUEMgbWFya2VyCgptYXRfbmV1cm9uID0gYygiUkJGT1gzIiwiU1lQIiwiRExHNDUiLCJWQU1QMSIsIlZBTVAyIiwiVFVCQjMiLCJTWVQxIiwiQlNOIiwiSE9NRVIxIiwiU0xDMTdBNiIpIAojIE5ldU4gaXMgRk9YMyAtIFJCRk9YMwojIFBTRDk1IGFsc28gU1AtOTAgb3IgRExHNAojIFZHTFVUMiBpcyBTTEMxN0E2CkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBtYXRfbmV1cm9uLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCiMgY2x1c3RlciA0IGFsc28gc2hvdyBtYXR1cmUgbmV1cm9uIG1hcmtlcnMKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBtYXRfbmV1cm9uKStSb3RhdGVkQXhpcygpCiMgZXhjaXRhdG9yeSBuZXVyb24gbWFya2VycwpleCA9IGMoIkdSSUEyIiwiR1JJQTEiLCJHUklBNCIsIkdSSU4xIiwiR1JJTjJCIiwiR1JJTjJBIiwiR1JJTjNBIiwiR1JJTjMiLCJHUklQMSIsIkNBTUsyQSIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBleCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGV4KStSb3RhdGVkQXhpcygpCiMgaW5oaWJpdG9yeSBuZXVyb24gbWFya2VycwppbmggPSBjKCJHQUQxIiwiR0FEMiIsICJHQVQxIiwiUFZBTEIiLCJHQUJSMiIsIkdBQlIxIiwiR0JSUjEiLCJHQUJSQjIiLCJHQUJSQjEiLCJHQUJSQjMiLCJHQUJSQTYiLCJHQUJSQTEiLCJHQUJSQTQiLCJUUkFLMiIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBpbmgsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBpbmgpK1JvdGF0ZWRBeGlzKCkKIyBjbHVzdGVyIDQgaXMgbW9yZSBleGNpdGF0b3J5IHRoYW4gaW5oYml0b3J5IGJ1dCBuZWl0aGVyIG1hcmtlciBzZXQgaGFzIG11Y2ggZXhwcmVzc2lvbiAKCiMjIyBnbGlhIG1hcmtlcnMKbWljcm9nbGlhID0gYygiUFRQUkMiLCJBSUYxIiwiQURHUkUxIikgICMgQURHUkUxIGlzIGEgbWljcm9nbGlhIG1hcmtlciBGNC84MCwgQ0Q0NSBpcyBQVFBSQywgZ2VuZSBuYW1lIElCQTEgaXMgQUlGMQphc3RvbGdOUENwcm9taWNybyA9IGMoIkdGQVAiLCJTMTAwQiIsIlNMQzFBMiIsIk1CUCIsIlNPWDEwIiwiU1BQMSIsIkRDWCIsIk5FVVJPRDEiLCJUQlIxIiwiUENOQSIsIk1LSTY3IiwiUFRQUkMiLCJBSUYxIiwiQURHUkUxIikKIyBub3RlIEdMVDEgaXMgRUFBVDIgd2hpY2ggaXMgU0xDMUEyIGdsdXRhdG1hdGUgdHJhbnNwb3J0ZXIKIyBlcGl0aGVsaWFsCmVwaSA9IGMoIkhFUzEiLCJIRVM1IiwiU09YMiIsIlNPWDEwIiwiTkVTIiwiQ0RIMSIsIk5PVENIMSIpICMgZS1jYWRoZXJpbiBpcyBDREgxCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gYXN0b2xnTlBDcHJvbWljcm8sIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBhc3RvbGdOUENwcm9taWNybywgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykrUm90YXRlZEF4aXMoKQojIGNsdXN0ZXIgNCBpcyBtb3JlIGV4Y2l0YXRvcnkgdGhhbiBpbmhiaXRvcnkgYnV0IG5laXRoZXIgbWFya2VyIHNldCBoYXMgbXVjaCBleHByZXNzaW9uIApEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZXBpLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZXBpLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKStSb3RhdGVkQXhpcygpCgojIGFsc28gYWRkIFJhZGlhbCBnbGlhIG1hcmtlciBvdmVybGFwIHdpdGggR2xpYSBhbmQgTmV1cm9ucwoKZmVhdHVyZXMgPC0gYygiUFRQUkMiLCJBSUYxIiwiQURHUkUxIiwgIlZJTSIsICJUTkMiLCJQVFBSWjEiLCJGQU0xMDdBIiwiSE9QWCIsIkxJRlIiLAogICAgICAgICAgICAgICJJVEdCNSIsIklMNlNUIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVzLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZXMsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpK1JvdGF0ZWRBeGlzKCkKCgpgYGAKCgpMYWJlbCB0aGUgTmV1cm9uMiBGQUNTIHBvcHVsYXRpb24gCgpgYGB7cn0KCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuNicKY2x1c3Rlci5pZHMgPC0gYygiTmV1cm9uczEiLCJpbW1hdHVyZU5ldXJvbnMxIiwiTmV1cm9uczIiLAogICAgICAgICAgICAgICAgICJPdGhlciIsIkRBbmV1cm9uczEiLCJEQW5ldXJvbnMyIiwiaW1tYXR1cmVOZXVyb25zMiIsCiAgICAgICAgICAgICAgICAgIkRBbmV1cm9uczMiLCJSRyIsImltbWF0dXJlTmV1cm9uczEiLCJFcGl0aGVsaWFsIiwiRW5kb3RoZWxpYWwiKQp1bmlxdWUoc2V1LnEkUk5BX3Nubl9yZXMuMC42KQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUucSkKc2V1LnEgPC0gUmVuYW1lSWRlbnRzKHNldS5xLCBjbHVzdGVyLmlkcykKc2V1LnEkc3ViZ3JvdXBzIDwtIElkZW50cyhzZXUucSkKCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdzdWJncm91cHMnLCByZXBlbCA9IFRSVUUpCgoKIyBsYWJlbCBhZ2FpbiB3aXRoIGp1c3QgbnVtYmVyaW5nCiMgdGhlbiBJJ2xsIGZpbmQgbWFya2VycyBpbiBwYWlycyB0byBkaXN0aW5ndWlzaCBncm91cHMuIAoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC42JwpjbHVzdGVyLmlkcyA8LSBjKCJOZXVyb25zMSIsIk5ldXJvbnMyIiwiTmV1cm9uczMiLAogICAgICAgICAgICAgICAgICJPdGhlciIsIkRBbmV1cm9uczEiLCJEQW5ldXJvbnMyIiwiTmV1cm9uczQiLAogICAgICAgICAgICAgICAgICJEQW5ldXJvbnMzIiwiUkciLCJOZXVyb25zMiIsIkVwaXRoZWxpYWwiLCJFbmRvdGhlbGlhbCIpCnVuaXF1ZShzZXUucSRSTkFfc25uX3Jlcy4wLjYpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRudW1iZXIuZ3JvdXBzIDwtIElkZW50cyhzZXUucSkKCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdudW1iZXIuZ3JvdXBzJywgcmVwZWwgPSBUUlVFKQoKCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvTmV1cm9uczJMYWJlbHNTZXUzMDA5MjAyMi5SRFMiKQoKCgpgYGAKCgoKRmluZCBtYXJrZXJzIGluIHBhaXJzIHRvIGdvIGJhY2sgYW5kIGNsYXNzaWZ5IHRoZSBzdWJncm91cHMuCldpbGwgbmVlZCB0byByZXR1cm4gdG8gdGhpcyBmb3IgTmV1cm9uczEgRkFDUwoKTmV1cm9ucyA1IGFuZCBOZXVyb25zMiBoYWQgc2ltaWxhciBtYXJrZXJzIGFuZCB3ZXJlIG1lcmdlZApTdWJzZXQgYWdhaW4KCmBgYHtyfQoKIyBmYXN0ZXIgdG8gbWFrZSBhIHN1YnNldCBvYmplY3RzIG9mIG9ubHkgbmV1cm9ucyBhbmQgdXNlIGZpbmQgYWxsIG1hcmtlcnMKCm5ldXJvbi5zdWIgPC0gc3Vic2V0KHNldS5xLCBpZGVudHMgPSBjKCJOZXVyb25zMSIsIk5ldXJvbnMyIiwiTmV1cm9uczMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTmV1cm9uczQiKSkKCm5ldXJvbi5zdWIubWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhuZXVyb24uc3ViKQoKdG9wNSA8LSBuZXVyb24uc3ViLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuPTUsIHd0ID0gYXZnX2xvZzJGQykKRG9IZWF0bWFwKG5ldXJvbi5zdWIsIGZlYXR1cmVzID0gdG9wNSRnZW5lLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCgpEb3RQbG90KG5ldXJvbi5zdWIsIGZlYXR1cmVzID0gdG9wNSRnZW5lKSArIFJvdGF0ZWRBeGlzKCkKCgojIG5ldXJvbnMgNSB3YXMgam9pbmVkIHRvIE5ldXJvbnMgMgojIGNsdXN0ZXIgbWFya2VycyBkb25lIGFnYWluCgojIE5ldXJvbnMxIDogTUdQICh0YXJnZXRpbmcgbmV1cmFsIHByb2plY3Rpb25zIEJNUCBzaWduYWxpbmcpLCBIUEQgKGV4Y2l0YXRvcnkgYW5kIGluaWJpdG9yeSwgY291bGQgYmUgY2VsbCBhZGhlc2lvbiBvciBhcG9wdG9zaXMpLCBNU1gxICh0cmFuc2NyaXB0aW9uIGZhY3RvciBCTVAgc2lnbmFsaW5nLCBtaWRicmFpbiBtYXJrZXIsIGRldmVsb3BtZW50YWwpLCBDWVAxQjEgKHJlZG94IGhvbWVvc3RhdGlzKSAgIAojIE1HUCwgSFBELCBNU1gxLCBDWVAxQjEKCiMgdGhlcmUgaXNuJ3QgYSBnb29kIHByb3BvcnRpb24gb2YgY2VsbHMgZXhwcmVzc2lvbiBhbnkgb2YgdGhlIG1hcmtlcnMsIE5ldXJvbnMyIGhhcyB0aGUgYmVzdCBhbW91bnQKCiMgTmV1cm9uczI6IFRGUDEyIHNlcmluZSBwcm90ZWFzZSBtZWxhdG9uaW4gY29udmVyc2lvbiwgUFROIChjeXRva2luZSBzaWduYWxpbmcpLCBMWWdIIChpbmhhbmNlcyBuQUNoUnMpLCBTMTAwQTEwIChtb2R1bGF0ZXMgc2Vyb3RvbmluIHJlY2VwdG9yKSwgSUZJMjcgKGFudGl2aXJhbCBhY3Rpdml0eSkKI1RGUDEyLFBUTiwgTFk2SCwgUzEwMEExMCwgSUZJMjcgCgojIE5ldXJvbnMzOiBTT1g0LCBBU0NMMSAobmV1cm9nZW5lc2lzKSwKCiMgU09YNCBpcyBhIG1hcmtlciBvZiAzIGJ1dCBoYXMgaGlnaCBleHByZXNzaW9uIGluIDQgYXMgd2VsbCAKCiMgTmV1cm9uczQ6IFBDQVQ0IChub3Qgbm90ZWQgaW4gbmV1cm9ucyksIFRQSDEgKDVIVCBzeW50aGVzaXMpLCBHSzUgKG5ldXJvbmFsIG1haW50YWluYW5jZSksIFNTVCAoc29tYXRvc3RhdGluLCBHQUJBIHNwaWtlIHJlZ3VsYXRpb24pLCBUVFIgKG5ldXJhbCBwcm90ZWN0aXZlIGluIEFEKQojIFBDQVQ0LCBUUEgxLCBHSzUsIFNTVCwgVFRSCgojIG1hcmtlcnMgdG8gdXNlCgojIE5ldXJvbnMxOiBNU1gxLCBDWVAxQjEgICAgICAKIyBOZXVyb25zMjogTFk2SCwgUzEwMEExMCAgICAgCiMgTmV1cm9uczM6IFNPWDQsIEFTQ0wxIChOZXVyb2dlbmVzaXMpIEltbWF0dXJlCiMgTmV1cm9uczQ6IEdLNSwgU1NUICAgICAgICAgICAgICAgICAgICAgICAgIE1vcmUgbWF0dXJlCgoKCiMgTWF5YmUgbmV1cm9ucyAxIGFuZCAyIGNvdWxkIGJlIG1lcmdlZAoKIyBsZXRzIHNlZSBob3cgdGhlIG1hcmtlcnMgd291bGQgbG9vawoKbmV1cm9ucy4xYW5kMiA8LSBGaW5kTWFya2VycyhuZXVyb24uc3ViLCBpZGVudC4xID0gYygiTmV1cm9uczEiLCJOZXVyb25zMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjIgPSBjKCJOZXVyb25zMyIsIk5ldXJvbnM0IikpCgp0b3AxMCA8LSBuZXVyb25zLjFhbmQyICU+JSB0b3BfbihuPTEwLCB3dCA9IGF2Z19sb2cyRkMpCmZ0LnVwIDwtIHJvd25hbWVzKHRvcDEwKSAjIHVwIGluIE5ldXJvbnMxIGFuZCAzCnRvcDEwIDwtIG5ldXJvbnMuMWFuZDIgJT4lIHRvcF9uKG49LTEwLCB3dCA9IGF2Z19sb2cyRkMpCmZ0LmRvd24gPC0gcm93bmFtZXModG9wMTApCmZlYXR1cmVzIDwtIGMoZnQudXAsZnQuZG93bikKCkRvSGVhdG1hcChuZXVyb24uc3ViLCBmZWF0dXJlcyA9IGZlYXR1cmVzLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCgpEb3RQbG90KG5ldXJvbi5zdWIsIGZlYXR1cmVzID0gZmVhdHVyZXMpICsgUm90YXRlZEF4aXMoKQoKIyBhbGwgdGhlIG1hcmtlcnMgd2VyZSB1cCByZWd1bGF0ZWQgaW4gbmV1cm9uczIgYW5kIG5vdCByZWFsbHkgbmV1cm9uczEKIyBJJ2xsIGtlZXAgdGhlbSBzZXBhcmF0ZWQKCgpgYGAKCgpVc2Ugc3ViZ3JvdXBpbmcgYW5kIGZpbmQgY2x1c3RlciBtYXJrZXJzIHRvIGxvb2sgYXQgbmV1cm9uYWwgc3VidHlwZXMuCgpgYGB7cn0KCiMgZmFzdGVyIHRvIG1ha2UgYSBzdWJzZXQgb2JqZWN0cyBvZiBvbmx5IG5ldXJvbnMgYW5kIHVzZSBmaW5kIGFsbCBtYXJrZXJzCgpuZXVyb24uc3ViIDwtIHN1YnNldChzZXUucSwgaWRlbnRzID0gYygiREFuZXVyb25zMSIsIkRBbmV1cm9uczIiLCJEQW5ldXJvbnMzIikpCgpuZXVyb24uc3ViLm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMobmV1cm9uLnN1YikKCnRvcDUgPC0gbmV1cm9uLnN1Yi5tYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obj01LCB3dCA9IGF2Z19sb2cyRkMpCkRvSGVhdG1hcChuZXVyb24uc3ViLCBmZWF0dXJlcyA9IHRvcDUkZ2VuZSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQoKRG90UGxvdChuZXVyb24uc3ViLCBmZWF0dXJlcyA9IHRvcDUkZ2VuZSkgKyBSb3RhdGVkQXhpcygpCgojIG1hcmtlcnMgYXJlIG11Y2ggY2xlYXJlciBmb3IgdGhlIERBIG5ldXJvbiBzdWJncm91cHMKCiMgREEgbmV1cm9ucyAxOiBXSUYxLCBDWVAxQjEsIElHRkJQMywgSFBELCBXRklLS04yCiMgV0lGMSAoc2VjcmV0ZWQgV05UIGluaGliaXRvciwgcHJvbW90ZXMgcmVnZW5lcmF0aW9uKSwgQ1lQMUIxIChyZWRveCBob21lb3N0YXRpcyksIElHRkJQMyAocHJvbGFjdGluIHNlY3JldGlvbiByZWd1bGF0aW9uIGh5cG90aGFsbXVzKSwgSFBEIChuZXVybyBwcm90ZWN0aXZlKSwgV0ZJS0tOMiAoUmVjZXB0b3IgZm9yIFROQykKIyBEQSBuZXVyb25zMjogQ0RINywgUlVOWDFUMSwgQVNDTDEsIERMSzEsIE1FRzMKIyBDREg3IChuZXVybyBjaXJjdWl0cnkgZGV2ZWxvcG1lbnQsIFNFTUEpLCBSVU5YMVQxIChuZXVyb25hbCBkaWZmZXJlbnRpYXRpb24pLCBBU0NMMSAobmV1cm9uYWwgZGlmZmVyZW50aWF0aW9uKSwgRExLMSAobmV1cmFsIGRpZmZlcmVudGF0aW9uKSwgTUVHMyAobmV1cmFsIGhvbWVvc3RhdGlzKSAgCiMgREEgbmV1cm9uczM6IFBDQVQ0LCBORVVST0QxLCBOQ0tBUDUsIEdLNSwgU1NUCiMgUENBVDQgKGRlbmRyaXRpYyBncm93dGgpLCBORVVST0QxIChuZXVyYWwgZGlmZmVyZW50YXRpb24pLCBOQ0tBUDUgKEV4Y2l0b3J5IG5ldXJvbnMpLCBHSzUgKFRIICksIFNTVCAocmVndWFsYXRlcyBzcGlrZSB0aW1lcykKCiMgREEgbmV1cm9uczE6IENZUDFCMSwgSUdGQlAzCiMgREEgbmV1cm9uczI6IFJVTlgxVDEsIEFTQ0wxCiMgREEgbmV1cm9uczM6IE5FVVJPRDEsIE5DS0FQNQoKCmBgYAoKCk5hbWUgdGhlIE5ldXJvbnMyIEZBQ1MgcG9wdWxhdGlvbiB3aXRoIHRoZSBOZXVyb24gc3VidHlwZSBsYXR0ZXIuCgpgYGB7cn0KCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuNicKY2x1c3Rlci5pZHMgPC0gYygiTmV1cm9ucy1NU1gxIiwiTmV1cm9ucy1MWTZIIiwiTmV1cm9ucy1BU0NMMSIsCiAgICAgICAgICAgICAgICAgIk90aGVyIiwiREFuZXVyb25zLUNZUDFCMSIsIkRBbmV1cm9ucy1BU0NMMSIsIk5ldXJvbnMtR0s1IiwKICAgICAgICAgICAgICAgICAiREFuZXVyb25zLU5FVVJPRDEiLCJSRyIsIk5ldXJvbnMtTFk2SCIsIkVwaXRoZWxpYWwiLCJFbmRvdGhlbGlhbCIpCgp1bmlxdWUoc2V1LnEkUk5BX3Nubl9yZXMuMC42KQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUucSkKc2V1LnEgPC0gUmVuYW1lSWRlbnRzKHNldS5xLCBjbHVzdGVyLmlkcykKc2V1LnEkY2VsbHN1Ymdyb3VwcyA8LSBJZGVudHMoc2V1LnEpCgpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnY2VsbHN1Ymdyb3VwcycsIHJlcGVsID0gVFJVRSkKCgpgYGAKCgoJcHJlZHVjdGlvbiBzdW1tYXJ5CUNlbGxfVHlwZXMKMAluZXVyb25zCU5ldXJvbnMKMQlOUEMvb2xpZ28vYXN0cm8vbmV1cm9ucy9SRwlOZXVyb25zCjIJbmV1cm9ucwlOZXVyb25zCjMJTlBDL1JHL0FzdHJvCU5ldXJvbnMKNAlSRy9lbmRvCVJHCjUJREEgbmV1cm9ucwlOZXVyb25zCjYJTmV1cm9ucwlOZXVyb25zCjcJREEgbmV1cm9ucwlOZXVyb25zCjgJUkcvZW5kbwlFbmRvdGhlbGlhbAo5CVJHL2FzdHJvCUFzdHJvCjEwCW9wYy9ucGMvYXN0cm8vbmV1cm9ucwlOZXVyb25zCjExCW5ldXJvbnMvZGl2aWRpbmcvUkcJTmV1cm9ucwoKQ2x1c3RlcmluZyBoaWdoZXIgcmVzCnJlcyAxLjIJCjAJTmV1cm9ucwoxCU5ldXJvbnMKMglOZXVyb25zCjMJQXN0cm8vUkcvTmV1cm9ucwo0CVJHL0FzdHJvL05ldXJvbnMKNQlSRy9OZXVyb25zL05QQwo2CVJHL05QQy9FbmRvCjcJTmV1cm9ucyBEQQo4CU5ldXJvbnMKOQlOZXVyb25zIERBCjEwCU5ldXJvbnMgREEKMTEJUkcvRW5kbwoxMglSRy9Bc3RybyAKMTMJUkcvQXN0cm8vTmV1cm9ucwoxNAlOZXVyb25zL1JHCgoKCgpgYGB7cn0KCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjEuMicKIyBoaWdobGlnaHQgdGhlIERBIG5ldXJvbnMKY2x1c3Rlci5pZHMgPC0gYygiTmV1cm9ucyIsIk5ldXJvbnMiLCJOUEMiLAogICAgICAgICAgICAgICAgICJOZXVyb25zIiwiTmV1cm9ucyIsIk5ldXJvbnMiLAogICAgICAgICAgICAgICAgICJOZXVyb25zIERBIiwiTmV1cm9ucyIsIk5ldXJvbnMgREEiLAogICAgICAgICAgICAgICAgICJOZXVyb25zIERBIiwgIk5ldXJvbnMgREEiLCJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgIlJHL0FzdHJvIiwiUkcvQXN0cm8vTmV1cm9ucyIsIk5ldXJvbnMvUkciKQpjbHVzdGVyLmlkcyA8LSBjKCJOZXVyb25zIiwiTmV1cm9ucyIsIk5ldXJvbnMiLAogICAgICAgICAgICAgICAgICJOZXVyb25zIiwiTmV1cm9ucyIsIk5QQyIsCiAgICAgICAgICAgICAgICAgIk5ldXJvbnMiLCJOZXVyb25zIiwiTmV1cm9ucyIsCiAgICAgICAgICAgICAgICAgIk5ldXJvbnMiLCAiTmV1cm9ucyIsIkVuZG90aGVsaWFsIiwKICAgICAgICAgICAgICAgICAiQXN0cm9jeXRlcyIsIlJhZGlhbCBHbGlhIiwiUmFkaWFsIEdsaWEiKQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUucSkKc2V1LnEgPC0gUmVuYW1lSWRlbnRzKHNldS5xLCBjbHVzdGVyLmlkcykKc2V1LnEkQ2VsbF9UeXBlcyA8LSBJZGVudHMoc2V1LnEpCgpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnQ2VsbF9UeXBlcycsIHJlcGVsID0gVFJVRSkKI0RpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4xLjInLCByZXBlbCA9IFRSVUUpCgojY2x1c3RyZWUoc2V1LnEpCgoKYGBgCgoKYGBge3J9CgojIHNhdmUgdGhlIE5ldXJvbnMyIHdpdGggbGFiZWxzCgpzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL05ldXJvbnMyTGFiZWxzU2V1MzAwOTIwMjIuUkRTIikKCmBgYAoKCkZBQ1MgcG9wdWxhdGlvbiBHbGlhMSAoc2hvdWxkIGJlIGFzdHJvY3l0ZXMpCgpgYGB7cn0KIyBleHBsb3JlIGZpbHRlcmluZwpzZXUgPC0gR2xpYTEKc2V1CiMgClZsblBsb3Qoc2V1LCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIG5jb2wgPSAzKQoKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIpLCB5Lm1heCA9IDEwMDApClZsblBsb3Qoc2V1LCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiKSwgeS5tYXggPSA1MDApClZsblBsb3Qoc2V1LCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuQ291bnRfUk5BIiksIHkubWF4ID0gMjAwMCkKCiMgZmlsdGVyIG1vcmUgY2VsbHMKCnNldS5mdCA8LSBzdWJzZXQoc2V1LCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiAzMDAgJiBuQ291bnRfUk5BID4gNTAwICYgbkNvdW50X1JOQSA8IDEwMDAwKSAKc2V1LmZ0CgojIHN0aWxsIGEgbG90IG9mIGNlbGxzIDQ3Mjk1CiMgd2lsbCBsaWtlbHkgcmVtb3ZlIGEgbG90IG1vcmUgd2l0aCB0aGUgZG91YmxldCBmaW5kZXIKCgpgYGAKCmBgYHtyfQoKc2F2ZVJEUyhzZXUuZnQsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTFBc3Ryb1NldTAxMTAyMDIyLlJEUyIpCgpzZXUuZnQgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL0dsaWExQXN0cm9TZXUwMTEwMjAyMi5SRFMiKQoKc2V1LmZ0IDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMUFzdHJvU2V1MDExMDIwMjIuUkRTIikKCmBgYAoKRG91YmxldCBmaW5kZXIKCmBgYHtyfQoKc3VwcHJlc3NNZXNzYWdlcyhyZXF1aXJlKERvdWJsZXRGaW5kZXIpKQoKIyBmaWx0ZXJpbmcgb3V0IE1BTEFUMSBhbmQgbWl0b2Nob25kcmlhbCBnZW5lcwoKc2V1LmZ0IDwtIHNldS5mdFshZ3JlcGwoIk1BTEFUMSIsIHJvd25hbWVzKHNldS5mdCkpLCBdCnNldS5mdCA8LSBzZXUuZnRbIWdyZXBsKCJeTVQtIiwgcm93bmFtZXMoc2V1LmZ0KSksIF0KCiMgbGlrZSBpbiB0aGUgdHV0b3JpYWwgSSdtIGZvbGxvd2luZyBNQUxBVDEgaXMgdGhlIHRvcCBtb3N0IGV4cHJlc3NlZCBnZW5lLiAgVGhlIHRvcCBnZW5lcyBhcmUgYSBsb3Qgb2YgTVQgYW5kIFJpYm9zb21hbCBnZW5lcwoKc2V1LmZ0W1sicGVyY2VudC5yYiJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChzZXUuZnQsIHBhdHRlcm4gPSAiXlJQIikKCiMgZG93biBzYW1wbGUgdGhlcmUgYXJlIHRvbyBtYW55IGNlbGxzIHRvIHJ1biBkb3VibGV0IGZpbmRlcgpzZXUuc3ViIDwtIHN1YnNldChzZXUuZnQsIGRvd25zYW1wbGUgPSAyMDAwMCkKCnNldS5kID0gTm9ybWFsaXplRGF0YShzZXUuc3ViKQpzZXUuZCA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldS5kLCB2ZXJib3NlID0gRikKc2V1LmQgPSBTY2FsZURhdGEoc2V1LmQsIHZhcnMudG8ucmVncmVzcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJwZXJjZW50Lm10IiksCiAgICB2ZXJib3NlID0gRikKc2V1LmQgPSBSdW5QQ0Eoc2V1LmQsIHZlcmJvc2UgPSBGLCBucGNzID0gMTUpCnNldS5kID0gUnVuVU1BUChzZXUuZCwgZGltcyA9IDE6MTAsIHZlcmJvc2UgPSBGKQoKbkV4cCA8LSByb3VuZChuY29sKHNldS5kKSAqIDAuMTUpICAjIGV4cGVjdCBtb3JlIGRvdWJsZXRzIGJlY2F1c2UgdGhlcmUgaXMgYSBsb3QgbW9yZSBjZWxscwpzZXUuZCA8LSBkb3VibGV0RmluZGVyX3YzKHNldS5kLCBwTiA9IDAuMjUsIHBLID0gMC4wOSwgbkV4cCA9IG5FeHAsIFBDcyA9IDE6MTApCiMgdGhlIG1lbW9yeSBsaW1pdCBpcyByZWFjaGVkIGhlcmUgLSBJIGNvdWxkIHJ1biBvbiBjb21wdXRlIGNhbmFkYQojIEZvciBub3cgSSdsbCBkb3duc2FtcGxlCiMgdGhpcyB3b3JrcwoKIyBuYW1lIG9mIHRoZSBERiBwcmVkaWN0aW9uIGNhbiBjaGFuZ2UsIHNvIGV4dHJhY3QgdGhlIGNvcnJlY3QgY29sdW1uIG5hbWUuCkRGLm5hbWUgPSBjb2xuYW1lcyhzZXUuZEBtZXRhLmRhdGEpW2dyZXBsKCJERi5jbGFzc2lmaWNhdGlvbiIsIGNvbG5hbWVzKHNldS5kQG1ldGEuZGF0YSkpXQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChuY29sID0gMiwgRGltUGxvdChzZXUuZCwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpICsgTm9BeGVzKCksCiAgICBEaW1QbG90KHNldS5kLCBncm91cC5ieSA9IERGLm5hbWUpICsgTm9BeGVzKCkpCgpWbG5QbG90KHNldS5kLCBmZWF0dXJlcyA9ICJuRmVhdHVyZV9STkEiLCBncm91cC5ieSA9IERGLm5hbWUsIHB0LnNpemUgPSAwLjEpCgpgYGAKCgoKClJlbW92ZSB0aGUgZG91YmxldCBjZWxscwoKYGBge3J9CnNldS5kIDwtIHNldS5kWywgc2V1LmRAbWV0YS5kYXRhWywgREYubmFtZV09PSAiU2luZ2xldCJdCmRpbShzZXUuZCkKZGltKHNldS5zdWIpCgojIDIwMDAwIHByZSBmaWx0ZXIKIyBjcmVhdGVzIHRoZSBleHBlY3RlZCBwZXJjZW50YWdlCgpgYGAKClJlcGVhdCB3b3JrZmxvdyB3aXRoIGRvdWJsZXQgcmVtb3ZlZCBkYXRhIGFuZCBmaW5kIGNsdXN0ZXJzIGZvciAKCmBgYHtyfQpzZXUgPC0gTm9ybWFsaXplRGF0YShzZXUuZCwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCnNldSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzZXUsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgbmZlYXR1cmVzID0gMjAwMCkKc2V1IDwtIFNjYWxlRGF0YShzZXUpCnNldSA8LSBSdW5QQ0Eoc2V1KQpzZXUgPC0gUnVuVU1BUChzZXUsIHJlZHVjdGlvbiA9ICJwY2EiLCBuLm5laWdoYm9ycyA9IDQzLCBkaW1zID0gMTozMCkKRGltUGxvdChzZXUsIHJlZHVjdGlvbiA9ICJ1bWFwIikKCnNldS5xIDwtIEZpbmROZWlnaGJvcnMoc2V1LCBkaW1zID0gMToyNSwgay5wYXJhbSA9IDQzKQpzZXUucSA8LSBGaW5kQ2x1c3RlcnMoc2V1LnEsIHJlc29sdXRpb24gPSBjKDAsMC4yLDAuNCwwLjYpKQpzZXUucSA8LSBGaW5kQ2x1c3RlcnMoc2V1LnEsIHJlc29sdXRpb24gPSBjKDAsMC4wNSwwLjEsMC44KSkKbGlicmFyeShjbHVzdHJlZSkKY2x1c3RyZWUoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC4wNScpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC4xJykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjInKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNCcpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjgnKQoKCgoKYGBgCkxvb2sgYXQgc29tZSBleHByZXNzaW9uIG1hcmtlcnMgaW4gYSBmZWF0dXJlIHBsb3QKCmBgYHtyfQojIGdlbmVzIHJlcG9ydGVkIHVwIGluIEFzdHJvY3l0ZXMKRmVhdHVyZVBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gYygiR0ZBUCIsIlMxMDBCIiwiQVFQNCIsIlNMQzFBMyIsIkdKQTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBUE9FIiwiVEVBRDEiLCJHU1RBNCIsIlNPWDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJWSU0iLCJITUcyMEEiLCJBTERIMUwxIikpCiMgYWxtb3N0IG5vIEdGQVAgZXhwcmVzc2lvbiBhbmQgbG90cyBvZiBTMTAwQiBldmVyeXdoZXJlCgpgYGAKCgoKUHJlZGljdCBjZWxsIHR5cGVzCgpgYGB7cn0KCgojIFNOQ0EgYW5kIGNvbnRyb2wgbWlkYnJhaW4gb3JnYW5vaWRzIDE2NSBkYXlzIGluIGN1bHR1cmUKTUJPIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FTVDIzX0JyYWluQ29tbS9NQk9jbHVzdGVyc19uYW1lczI5MDcyMDIxLnJkcyIpCgojIE1pZGJyYWluICBBSVcwMDIgMTIwIGRheXMgaW4gY3VsdHVyZQpBSVdNQk8gPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvQUlXdHJpbzEyMGRheXMvTU9pbnRlZ3JhdGVkQ2x1c3RlcksxMjNyZXMwLjgubmFtZXNfbm92MTZfMjAyMSIpCgojIE1pZGJyYWluIEFJVzAwMiA2MCBkYXlzIGluIGN1bHR1cmUKCkFJVzYwIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FJV3RyaW82MGRheXMvQVdJMDAyUGFya2luS09QaW5rS082MGRheXNfbGFiZWxzXzE0MDUyMDIyLnJkcyIpCgoKI2ZpcnN0IHByZWRpY3Qgd2l0aCB0aGUgTUJPIGRhdGEKSWRlbnRzKE1CTykgPC0gImNsdXN0ZXJfbGFiZWxzIgpEZWZhdWx0QXNzYXkoTUJPKSA8LSAiUk5BIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwpwcmludCgiZmluZGluZyByZWZlcmVuY2UgYW5jaG9ycyIpCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBNQk8gLHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gTUJPJGNsdXN0ZXJfbGFiZWxzKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRNQk9BU1QyMy5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnTUJPQVNUMjMucHJlZCcsIGxhYmVsID0gVFJVRSkKIAojIyBjaGVjayB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHByZWRpY3RlZCBpbiBlYWNoIGNsdXN0ZXIKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjgsIHNldS5xJE1CT0FTVDIzLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQoKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgY2x1c3RlcnMgZG9uJ3QgYnJlYWsgdXAgYnkgdGhlIHByZWRpY3RlZCBjZWxsIHR5cGVzCgojIyMjIyMjIyMjIyMgYW5vdGhlciBwcmVkaWN0aW9ucyBub3cgdXNpbmcgdGhlIEFJVyBvcmdhbm9pZHMKCklkZW50cyhBSVdNQk8pIDwtICJyZXMwOG5hbWVzIgpEZWZhdWx0QXNzYXkoQUlXTUJPKSA8LSAiUk5BIgoKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IEFJV01CTyAscXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBBSVdNQk8kcmVzMDhuYW1lcykKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCiMgYWRkIG5ldyBkYXRhc2xvdCBmb3IgTUJPIHByZWRpY3RlZCBJRCB0byBtYWtlIHRoZSBuZXh0IHByZWRpY3Rpb24Kc2V1LnEkTUJPQUlXLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdNQk9BSVcucHJlZCcsIGxhYmVsID0gVFJVRSkKIAojIyBjaGVjayB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHByZWRpY3RlZCBpbiBlYWNoIGNsdXN0ZXIKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjgsIHNldS5xJE1CT0FJVy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKCiMgdHJ5IGJhciBjaGFydApnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpCgojIHRoZSBwcmVkaWN0ZWQgY2VsbCB0eXBlcyBtYWtlIG1vcmUgc2Vuc2UgZnJvbSB0aGUgQUlXMDAyIG9yZ2Fub2lkCiMgbm93IHByZWRpY3Qgd2l0aCB0aGUgQUlXMDAyIDYwIGRheXMgb3JnYW5vaWQKCklkZW50cyhBSVc2MCkgPC0gImNsdXN0ZXIuaWRzIgpEZWZhdWx0QXNzYXkoQUlXNjApIDwtICJSTkEiCgphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gQUlXNjAsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gQUlXNjAkY2x1c3Rlci5pZHMpIApzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRBSVc2MC5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQUlXNjAucHJlZCcsIGxhYmVsID0gVFJVRSkKIAojIyBjaGVjayB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHByZWRpY3RlZCBpbiBlYWNoIGNsdXN0ZXIKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjgsIHNldS5xJEFJVzYwLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQoKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgc2F2ZSBvamJlY3Qgd2l0aCBwcmVkaWNpdG9ucwpzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL0dsaWExUHJlZGljdGlvbnNTZXUwMTEwMjAyMi5SRFMiKQoKCgoKYGBgCgoKUHJlZGljdCB3aXRoIHRoZSBicmFpbiBzY1JOQXNlcQoKYGBge3J9CgpwYXRod2F5IDwtICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvIgpzZXUucSA8LSByZWFkUkRTKHBhc3RlKHBhdGh3YXksIkdsaWExTGFibGVkU2V1MzAxMTAyMDIyLlJEUyIsc2VwID0gIiIpKQoKCiMgcmVhZCBpbiB0aGUgcmVmZXJlbmNlIGRhdGFzZXQKIyBmcm9tIEJoYWR1cmkgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCnNldS5yIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1B1YmxpY0RhdGEvQmhhZHVyaV93aG9sZUJyYWluL0JoYWR1cmlfbWlkYnJhaW5fc3RyaWF0dW0uUkRTIikKCklkZW50cyhzZXUucikgPC0gImNlbGxfY2x1c3RlciIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJGNlbGxfY2x1c3RlcikKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5taWQuc3RyaS5wcmVkIDwtIElkZW50cyhzZXUucSkKcHJpbnQodGFibGUoc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEubWlkLnN0cmkucHJlZCcpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycpCgojIGRvIHRoZSBwcmVkaWN0aW9ucyBkaWZmZXIgd2l0aCB0aGUgbWFpbiBjZWxsIHR5cGUgZ3JvdXBzIGluc3RlYWQgb2YgdGhlIGNsdXN0ZXIgaW4gdGhlIHJlZmVyZW5jZSBkYXRhPyAKSWRlbnRzKHNldS5yKSA8LSAiY2VsbF90eXBlIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1LnIsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gc2V1LnIkY2VsbF90eXBlKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAncHJlZGljdGVkLmlkJykKCmBgYAoKCgoKCmBgYHtyfQoKIyBBSVcwMDIgMTIwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJE1CT0FJVy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzEyMCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKSA8LSBOVUxMCmRmLnRvcC5haXcxMjAkSSA8LSByb3cubmFtZXMoZGYudG9wLmFpdzEyMCkKCiMgQUlXMDAyIDYwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJEFJVzYwLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAgPC1hcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzYwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXc2MCkgPC0gTlVMTApkZi50b3AuYWl3NjAkSSA8LSByb3cubmFtZXMoZGYudG9wLmFpdzYwKQoKCiMgQVNUMjMgMTY1IGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJE1CT0FTVDIzLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5BU1QyMyA8LSB0b3AucHJlZC5jZWxsdHlwZS5BU1QyM1tvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQVNUMjMpIDwtIE5VTEwKZGYudG9wLkFTVDIzJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykKCiMjIyBhZGQgaW4gdGhlIHByZWRpY3Rpb24gZnJvbSBicmFpbiBkYXRhIEJoYWR1cmkgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRCaGEubWlkLnN0cmkucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5CaGEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5CaGEgPC0gdG9wLnByZWQuY2VsbHR5cGUuQmhhW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkJoYSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5CaGEkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLkJoYSkgPC0gTlVMTApkZi50b3AuQmhhJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5CaGEpCgojIyB0aGVzZSBhcmUgY2FsY3VsYXRlZCBiZWxvdwojIyMgYWRkIGluIHRoZSBwcmVkaWN0aW9uIGZyb20gYnJhaW4gd2hvbGUgYnJhaW4gZGF0YSBCaGFkdXJpIG1pZGJyYWluIGRvd24gc2FtcGxlZAp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkQmhhKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkJoYTEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5CaGExIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkJoYTFbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQmhhJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkJoYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQmhhMSkgPC0gTlVMTApkZi50b3AuQmhhMSRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQmhhMSkKCnByZWQudGFibGUgPC0gbWVyZ2UoZGYudG9wLkFTVDIzLCBkZi50b3AuYWl3NjAsIGJ5ID0gJ0knLCBhbGwgPSBUUlVFKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5haXcxMjAsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGExLCBieSA9ICdJJykKcHJlZC50YWJsZQoKYGBgClRoZXNlIHByZWRpY3Rpb25zIGFyZSBub3QgZ29vZC4gIFRoZXJlIGFyZSBzZXZlcmFsIGFzdHJvY3l0ZSBtYXJrZXJzIGJ5IGV4cHJlc3Npb24gbGV2ZWxzLiAgRXZlcnl0aGluZyBpcyBwcmVkaWN0ZWQgYXMgUmFkaWFsIGdsaWEgb3Igb2xpZ28gZGVuZHJvY3l0ZXMKCgpUcnkgdG8gcHJlZGljdCB3aXRoIHRoZSB3aG9sZSBicmFpbiBhbmQgc2VlIGlmIGl0J3MgZGlmZmVyZW50CgpgYGB7cn0KIyByZWFkIGluIHRoZSByZWZlcmVuY2UgZGF0YXNldAojIGZyb20gQmhhZHVyaSBtaWRicmFpbiBhbmQgc3RyaWF0dW0Kc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9CaGFkdXJpX3dob2xlQnJhaW4vQmhhZHVyaV9kb3duc2FtcGxlLlJEUyIpCgpJZGVudHMoc2V1LnIpIDwtICJjZWxsX2NsdXN0ZXIiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRjZWxsX2NsdXN0ZXIpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRCaGEgPC0gSWRlbnRzKHNldS5xKQpwcmludCh0YWJsZShzZXUucSRCaGEpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEnKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdzdWJncm91cHMnKQoKCgoKCmBgYAoKCgoKVHJ5IHRvIHByZWRpY3Qgd2l0aCB0aGUgYXN0cm9jeXRlIEthbWF0aCBkYXRhCgpgYGB7cn0KCmFzdHJvLnJlZiA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9NYWNvc2tvX0RhdGEvUERfYXN0cm8uUmRzIikKIyBuZWVkIHRvIG1ha2UgUENBIGFuZCBVTUFQCmFzdHJvLnJlZiA8LSBOb3JtYWxpemVEYXRhKGFzdHJvLnJlZikKYXN0cm8ucmVmIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKGFzdHJvLnJlZiwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQphc3Ryby5yZWYgPC0gU2NhbGVEYXRhKGFzdHJvLnJlZikKYXN0cm8ucmVmIDwtIFJ1blBDQShhc3Ryby5yZWYpCmFzdHJvLnJlZiA8LSBSdW5VTUFQKGFzdHJvLnJlZiwgcmVkdWN0aW9uID0gInBjYSIsIG4ubmVpZ2hib3JzID0gMjA1LCBkaW1zID0gMToyNSkKCmNvbG5hbWVzKGFzdHJvLnJlZkBtZXRhLmRhdGEpCgoKSWRlbnRzKGFzdHJvLnJlZikgPC0gIkNlbGxfU3VidHlwZSIKRGVmYXVsdEFzc2F5KGFzdHJvLnJlZikgPC0gIlJOQSIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKcHJpbnQoImZpbmRpbmcgcmVmZXJlbmNlIGFuY2hvcnMiKQphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gYXN0cm8ucmVmICxxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyMCkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IGFzdHJvLnJlZiRDZWxsX1N1YnR5cGUsIGsud2VpZ2h0ID0gMTApCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJGFzdHJvLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdhc3Ryby5wcmVkJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRhc3Ryby5wcmVkKQoKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgTkEpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJGFzdHJvLnByZWQudGhyZXNoIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnYXN0cm8ucHJlZC50aHJlc2gnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJGFzdHJvLnByZWQudGhyZXNoKQoKIyAxOTk4NiBBc3Ryb19WSU1fVE5GU1JGMTJBIG5vIHRocmVzaG9sZCAgICAgIEFzdHJvX0dMWUFUTDIgMTQKIyA4Mzc2IEFzdHJvX1ZJTV9UTkZTUkYxMkEgICB3aXRoIDk1JSB0aHJlc2hvbGQKCgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkYXN0cm8ucHJlZC50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuYXN0cm8gPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5hc3RybyA8LSB0b3AucHJlZC5hc3Ryb1tvcmRlcih0b3AucHJlZC5hc3RybyRWYXIxLC10b3AucHJlZC5hc3RybyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYXN0cm8pIDwtIE5VTEwKCgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkYXN0cm8ucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5hc3RybyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFzdHJvIDwtIHRvcC5wcmVkLmFzdHJvW29yZGVyKHRvcC5wcmVkLmFzdHJvJFZhcjEsLXRvcC5wcmVkLmFzdHJvJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5hc3RybykgPC0gTlVMTAoKCgpgYGAKCgoKUG9zc2libGUgcHJlZGljdGVkIGluIG90aGVyIGNsdXN0ZXJzCgpgYGB7cn0KY2x1c3RyZWUoc2V1LnEpCgpgYGAKCk1ha2UgdGhlIHByZWRpY3Rpb24gdGFibGUgZm9yIGhpZ2ggcmVzb2x1dGlvbiBSZXMgMC44IDEyIGNsdXN0ZXJzCgpgYGB7cn0KIyBBSVcwMDIgMTIwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjgsIHNldS5xJE1CT0FJVy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzEyMCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKSA8LSBOVUxMCmRmLnRvcC5haXcxMjAkSSA8LSByb3cubmFtZXMoZGYudG9wLmFpdzEyMCkKCiMgQUlXMDAyIDYwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjgsIHNldS5xJEFJVzYwLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAgPC1hcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzYwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXc2MCkgPC0gTlVMTApkZi50b3AuYWl3NjAkSSA8LSByb3cubmFtZXMoZGYudG9wLmFpdzYwKQoKCiMgQVNUMjMgMTY1IGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjgsIHNldS5xJE1CT0FTVDIzLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5BU1QyMyA8LSB0b3AucHJlZC5jZWxsdHlwZS5BU1QyM1tvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQVNUMjMpIDwtIE5VTEwKZGYudG9wLkFTVDIzJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykKCiMjIyBhZGQgaW4gdGhlIHByZWRpY3Rpb24gZnJvbSBicmFpbiBkYXRhIEJoYWR1cmkgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC44LCBzZXUucSRCaGEubWlkLnN0cmkucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5CaGEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5CaGEgPC0gdG9wLnByZWQuY2VsbHR5cGUuQmhhW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkJoYSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5CaGEkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLkJoYSkgPC0gTlVMTApkZi50b3AuQmhhJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5CaGEpCgojIyB0aGVzZSBhcmUgY2FsY3VsYXRlZCBiZWxvdwojIyMgYWRkIGluIHRoZSBwcmVkaWN0aW9uIGZyb20gYnJhaW4gd2hvbGUgYnJhaW4gZGF0YSBCaGFkdXJpIG1pZGJyYWluIGRvd24gc2FtcGxlZAp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuOCwgc2V1LnEkQmhhKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkJoYTEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5CaGExIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkJoYTFbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQmhhJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkJoYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQmhhMSkgPC0gTlVMTApkZi50b3AuQmhhMSRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQmhhMSkKCnByZWQudGFibGUgPC0gbWVyZ2UoZGYudG9wLkFTVDIzLCBkZi50b3AuYWl3NjAsIGJ5ID0gJ0knLCBhbGwgPSBUUlVFKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5haXcxMjAsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGExLCBieSA9ICdJJykKcHJlZC50YWJsZQoKYGBgCgoKCgpMb29rIGF0IGNsdXN0ZXIgbWFya2VycwoKYGBge3J9CgpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4wLjInCkNsdXN0ZXJNYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHNldS5xLCBvbmx5LnBvcyA9IFRSVUUpCgp0b3A1IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obj01LCB3dCA9IGF2Z19sb2cyRkMpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSB0b3A1JGdlbmUsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKCndyaXRlLmNzdihDbHVzdGVyTWFya2VycywiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9HbGlhMUFzdHJvY3l0ZXNDbHVzdGVyTWFya2Vyc19uZXcuY3N2IikKCiMgZm9yIHRoZSByZXMgMC42IHRoZSBsYXJnZXJzIGdyb3VwcyAwIGFuZCAxIGRvbid0IGhhdmUgZ3JlYXQgbWFya2VycyBhbmQgbm9uZSBvZiB0aGUgbWFya2VycyBhcmUgcmVhbGx5IHZlcnkgZ29vZC4gIAojIEknbGwgcmVydW4gd2l0aCByZXMgMC4yCnVuaXF1ZShzZXUucSRSTkFfc25uX3Jlcy4wLjIpCgojIHN0aWxsIG5vdCBtdWNoIGJldHRlcgoKCgpgYGAKCgoKQ2hlY2sgY2VsbCB0eXBlIG1hcmtlcnMgd2l0aCBFbnJpY2hSCgpgYGB7cn0KCmxpYnJhcnkoZW5yaWNoUikKCnNldEVucmljaHJTaXRlKCJFbnJpY2hyIikgIyBIdW1hbiBnZW5lcwojIGxpc3Qgb2YgYWxsIHRoZSBkYXRhYmFzZXMKCiMgbGliYXJpZXMgd2l0aCBjZWxsIHR5cGVzCgpkYiA8LSBjKCdBbGxlbl9CcmFpbl9BdGxhc191cCcsJ0Rlc2NhcnRlc19DZWxsX1R5cGVzX2FuZF9UaXNzdWVfMjAyMScsCiAgICAgICAgJ0NlbGxNYXJrZXJfQXVnbWVudGVkXzIwMjEnLCdBemltdXRoX0NlbGxfVHlwZXNfMjAyMScpCgojIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IE5VTEwpCgojSSdsbCBydW4gdGhlIGNsdXN0ZXJzIG9uZSBhdCBhIHRpbWUKCk4xLmMwIDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA1ICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmMwJGdlbmUKCk4xLmMwLkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jMC5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmMwLkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzAuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzAuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKCgojIGNsdXN0ZXIgMCAtIENlbGwgdHlwZSBtYXJrZXIgbGlicmFyeSAtIEJyYWluIGFzdHJvY3l0ZSB0b3AgaGl0IGFuZCBlbWJyeW9uaWMgYXN0cm9jeXRlcwojIGdlbmUgbGlzdCBpbiB0ZXJtOiBicmFpbiBhc3Ryb2N5dGUJRUZFTVAxO05GSUE7TElYMTtQU0FQO0tJRjIxQTtTMTAwQjtDUllBQjtES0szCiMgZW1icnlvIGFzdHJvY3l0ZXMJU09YMjtCRVgxO0hNR0NTMTtQVFBSWjE7TElYMTtTMTAwQjtES0szO0lUTTJDCgojIGNsdXN0ZXIgMSAtIHN0ZW0gY2VsbCBwZXJpY3l0ZSAoYnJhaW4pLCBzdGVsYXRlLCBhc3Ryb2N5dGUKCiMgY2x1c3RlciAyIC0gaHlwb3RoYWxtdXMsIGVuZG90aGVsaWFsIGNlbGxzLCBtYWNyb3BoYWdlCiMgZW5kb3RoZWxpYWwgQ1NUQjtQUkVMSUQxO01UMVg7Q1JJUDI7UkhPQztUTUVNMTQxO01UMkE7UlBTMjg7Q0NEQzg1QjtFSUYzSTtSQlAxO0lEMTtDNE9SRjM7SUQzO1BDQkQxO01TWDE7UFBJQwoKIyBjbHVzdGVyIDMgLSBzbW9vdGggbXVzY2xlIGNlbGxzCgojIGNsdXN0ZXIgNCAtIE5LIGNlbGxzLCBmaWJyb2JsYXN0cwojIE5LIGNlbGxzIElUR0IxO1JBQjVDO0dTVFAxO1BEQ0Q1O0VFRjFCMjtUR09MTjI7U0RDQlA7TVQyQTtMREhBO1NOUlBEMjtZV0hBUTtaTkYzMjY7VE1TQjEwO0NDREM1MAojIGZpYnJvYmxhc3RzIAlDT0wzQTE7Q0FMRDE7Q09MNkEzCgojIGNsdXN0ZXIgNSAtIGVuZG90aGVsaWFsIGNlbGxzLCBOSyBjZWxscywgQ0Q4KwoKIyBjbHVzdGVyIDYgLSBzdHJvbWFsIGNlbGxzIGV1cnl0aHJvYmxhc3RzLCBub25lLW5ldXJvbmFsLCBvbGlnbwoKIyByZXJhbiBhbmQgbm93IHRoZXJlIGFyZSBvbmx5IDUgY2x1c3RlcnMKIyByZXBlYXQgY2hlY2tpbmcKCgpgYGAKCkNsdXN0ZXIgMCAtIGFzdHJvY3l0ZXMKQ2x1c3RlciAxIC0gcGVyaWN5dGUgYXN0cm9jeXRlICh3ZWFrIHN0aWxsKQpDbHVzdGVyIDIgLSBlbmRvdGhlbGlhbApDbHVzdGVyIDMgLSBzbW9vdGggbXVzY2xlCkNsdXN0ZXIgNCAtIE5LL2ZpYnJvYmxhc3QKQ2x1c3RlciA1IC0gZW5kb3RoZWxpYWwKQ2x1c3RlciA2IC0gbm9uLSBuZXVyb25hbAoKCgoKCgoKYGBge3J9CgpWbG5QbG90KHNldS5xLCBmZWF0dXJlcyA9IGMoIkNENDQiLCJJVEdCMSIsIlMxMDBCIiksIGdyb3VwLmJ5ID0gJ29yaWcuaWRlbnQnKQpWbG5QbG90KHNldS5mdCwgZmVhdHVyZXMgPSBjKCJDRDQ0IiwiSVRHQjEiLCJTMTAwQiIpLCBncm91cC5ieSA9ICdvcmlnLmlkZW50JykKCmBgYAoKQ2hlY2sgZXhwcmVzc2lvbiBvZiBrbm93biBtYXJrZXJzCgpgYGB7cn0KCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuMicKCmZlYXR1cmVfbGlzdCA9IGMoIk1LSTY3IiwiU09YMiIsIlBPVTVGMSIsIkRMWDIiLCJQQVg2IiwiU09YOSIsIkhFUzEiLCJORVMiLCJSQkZPWDMiLCJNQVAyIiwiTkNBTTEiLCJDRDI0IiwiR1JJQTIiLCJHUklOMkIiLCJHQUJCUjEiLCJHQUQxIiwiR0FEMiIsIkdBQlJBMSIsIkdBQlJCMiIsIlRIIiwiQUxESDFBMSIsIkxNWDFCIiwiTlI0QTIiLCJDT1JJTiIsIkNBTEIxIiwiS0NOSjYiLCJDWENSNCIsIklUR0E2IiwiU0xDMUEzIiwiQ0Q0NCIsIkFRUDQiLCJTMTAwQiIsICJQREdGUkEiLCJPTElHMiIsIk1CUCIsIkNMRE4xMSIsIlZJTSIsIlZDQU0xIikKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QpICtSb3RhdGVkQXhpcygpCgpQRF9wb3VsaW4gPSBjKCJUSCIsIlNMQzZBMyIsIlNMQzE4QTIiLCJTT1g2IiwiTkRORiIsIlNOQ0ciLCJBTERIMUExIiwiQ0FMQjEiLCJUQUNSMiIsIlNMQzE3QTYiLCJTTEMzMkExIiwiT1RYMiIsIkdSUCIsIkxQTCIsIkNDSyIsIlZJUCIpCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gUERfcG91bGluLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gUERfcG91bGluKStSb3RhdGVkQXhpcygpCgplYWxyeU5ldXIgPSBjKCJEQ1giLCJORVVST0QxIiwiVEJSMSIpCnByb2xpZmVyYXRpb24gPSBjKCJQQ05BIiwiTUtJNjciKQpuZXVyYWxzdGVtID0gYygiU09YMiIsIk5FUyIsIlBBWDYiLCJNQVNIMSIpCgpmZWF0dXJlX2xpc3QgPC0gYygiRENYIiwiTkVVUk9EMSIsIlRCUjEiLCJQQ05BIiwiTUtJNjciLCJTT1gyIiwiTkVTIiwiUEFYNiIsIk1BU0gxIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCkrUm90YXRlZEF4aXMoKQoKCm1hdF9uZXVyb24gPSBjKCJSQkZPWDMiLCJTWVAiLCJETEc0NSIsIlZBTVAxIiwiVkFNUDIiLCJUVUJCMyIsIlNZVDEiLCJCU04iLCJIT01FUjEiLCJTTEMxN0E2IikgCiMgTmV1TiBpcyBGT1gzIC0gUkJGT1gzCiMgUFNEOTUgYWxzbyBTUC05MCBvciBETEc0CiMgVkdMVVQyIGlzIFNMQzE3QTYKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IG1hdF9uZXVyb24sIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKIyBjbHVzdGVyIDQgYWxzbyBzaG93IG1hdHVyZSBuZXVyb24gbWFya2VycwpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IG1hdF9uZXVyb24pK1JvdGF0ZWRBeGlzKCkKIyBleGNpdGF0b3J5IG5ldXJvbiBtYXJrZXJzCmV4ID0gYygiR1JJQTIiLCJHUklBMSIsIkdSSUE0IiwiR1JJTjEiLCJHUklOMkIiLCJHUklOMkEiLCJHUklOM0EiLCJHUklOMyIsIkdSSVAxIiwiQ0FNSzJBIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGV4LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZXgpK1JvdGF0ZWRBeGlzKCkKIyBpbmhpYml0b3J5IG5ldXJvbiBtYXJrZXJzCmluaCA9IGMoIkdBRDEiLCJHQUQyIiwgIkdBVDEiLCJQVkFMQiIsIkdBQlIyIiwiR0FCUjEiLCJHQlJSMSIsIkdBQlJCMiIsIkdBQlJCMSIsIkdBQlJCMyIsIkdBQlJBNiIsIkdBQlJBMSIsIkdBQlJBNCIsIlRSQUsyIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGluaCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGluaCkrUm90YXRlZEF4aXMoKQojIGNsdXN0ZXIgNCBpcyBtb3JlIGV4Y2l0YXRvcnkgdGhhbiBpbmhiaXRvcnkgYnV0IG5laXRoZXIgbWFya2VyIHNldCBoYXMgbXVjaCBleHByZXNzaW9uIAoKIyMjIGdsaWEgbWFya2VycwptaWNyb2dsaWEgPSBjKCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiKSAgIyBBREdSRTEgaXMgYSBtaWNyb2dsaWEgbWFya2VyIEY0LzgwLCBDRDQ1IGlzIFBUUFJDLCBnZW5lIG5hbWUgSUJBMSBpcyBBSUYxCmFzdG9sZ05QQ3Byb21pY3JvID0gYygiR0ZBUCIsIlMxMDBCIiwiU0xDMUEyIiwiTUJQIiwiU09YMTAiLCJTUFAxIiwiRENYIiwiTkVVUk9EMSIsIlRCUjEiLCJQQ05BIiwiTUtJNjciLCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiKQojIG5vdGUgR0xUMSBpcyBFQUFUMiB3aGljaCBpcyBTTEMxQTIgZ2x1dGF0bWF0ZSB0cmFuc3BvcnRlcgojIGVwaXRoZWxpYWwKZXBpID0gYygiSEVTMSIsIkhFUzUiLCJTT1gyIiwiU09YMTAiLCJORVMiLCJDREgxIiwiTk9UQ0gxIikgIyBlLWNhZGhlcmluIGlzIENESDEKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBhc3RvbGdOUENwcm9taWNybywgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGFzdG9sZ05QQ3Byb21pY3JvKStSb3RhdGVkQXhpcygpCiMgY2x1c3RlciA0IGlzIG1vcmUgZXhjaXRhdG9yeSB0aGFuIGluaGJpdG9yeSBidXQgbmVpdGhlciBtYXJrZXIgc2V0IGhhcyBtdWNoIGV4cHJlc3Npb24gCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBlcGksIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBlcGkpK1JvdGF0ZWRBeGlzKCkKCiMgYWxzbyBhZGQgUmFkaWFsIGdsaWEgbWFya2VyIG92ZXJsYXAgd2l0aCBHbGlhIGFuZCBOZXVyb25zCgpmZWF0dXJlcyA8LSBjKCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiLCAiVklNIiwgIlROQyIsIlBUUFJaMSIsIkZBTTEwN0EiLCJIT1BYIiwiTElGUiIsCiAgICAgICAgICAgICAgIklUR0I1IiwiSUw2U1QiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZXMsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlcykrUm90YXRlZEF4aXMoKQoKCgoKYGBgCgpObyBUSCBleHByZXNzaW9uCgpjbHVzdGVycyA1IFZJTSBoaWdoZXN0LCBTMTAwIEIgCkNsdXN0ZXIgNCAKQ2x1c3RlciAzIGhhcyBzb21lIGNlbGxzIHdpdGggaGlnaCBPVFgyLCBORVMgaW5kaWNhdGVzIE5QQy9QcmVjdXJzb3JzCkNsdXN0ZXIgMiBoYXMgc29tZSBTT1gyIGFuZCBQQVg2IGluZGljYXRlcyBOUEMsIFZBTVAyIGluZGljYXRpbmcgbmV1cm9ucywgUzEwMEIgaGlnaGVzdCBhbmQgbW9zdCAtIGluZGljYXRlcyBhc3Ryb2N5dGVzLCBhbHNvIE1CUCBpbmRpY2F0ZXMgb2xpZ29zCkNsdXN0ZXIgMQpDbHVzdGVyIDAgCgoKTGFibGUgdGhlIGNsdXN0ZXJzIAoKYGBge3J9CgpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4wLjInCgojc2V1LnEgPC0gQnVpbGRDbHVzdGVyVHJlZShzZXUucSwgcmVvcmRlciA9IFRSVUUsIHJlb3JkZXIubnVtZXJpYyA9IFRSVUUpCnVuaXF1ZShzZXUucSRSTkFfc25uX3Jlcy4wLjIpCgpjbHVzdGVyLmlkcyA8LSBjKCJBc3Ryb2N5dGVzMSIsIkFzdHJvY3l0ZXMyIiwiUHJlY3Vyc29ycyIsIlJHMSIsIlJHMiIsIkVuZG90aGVsaWFsIikKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1LnEpCnNldS5xIDwtIFJlbmFtZUlkZW50cyhzZXUucSwgY2x1c3Rlci5pZHMpCnNldS5xJHN1Ymdyb3VwcyA8LSBJZGVudHMoc2V1LnEpCgojRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC4yJywgbGFiZWwgPSBUUlVFKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnc3ViZ3JvdXBzJywgcmVwZWwgPSBUUlVFKQoKIyBzb21ldGhpbmcgd2VpcmQgaXMgZ29pbmcgb24gaW4gdGhlIG9yZGVyCgojc2F2ZVJEUyhzZXUucSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMUxhYmxlZFNldTMwMTEwMjAyMi5SRFMiKQoKCmBgYAoKCgpDb21wYXJlIHRoZSBBc3Ryb2N5dGUgZ3JvdXBzIGFuZCBnZXQgc29tZSBtYXJrZXJzIGZvciBzdWIgZ3JvdXBzCgpgYGB7cn0KCgphc3Ryby5zdWIubWFya2VycyA8LSBGaW5kTWFya2VycyhzZXUucSwgaWRlbnQuMSA9ICJBc3Ryb2N5dGVzMSIsIGlkZW50LjIgPSAiQXN0cm9jeXRlczIiLCBvbmx5LnBvcyA9IEZBTFNFKQojdG9wNSA8LSBhc3Ryby5zdWIubWFya2VycyAlPiUgdG9wX24obj01LCB3dCA9IGF2Z19sb2cyRkMpCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gYygiUExDRzIiLCJQVFBSWjEiLCJTTkhHMjUiLCJWQ0FOIiwiTFVNIiwiRENOIiwiUzEwMDRBIiksIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKCmFzdHJvMiA8LSByb3duYW1lcyhhc3Ryby5zdWIubWFya2VycyAlPiUgZmlsdGVyKGF2Z19sb2cyRkMgPCAwLjA1KSkKCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gYygiUExDRzIiLCJQVFBSWjEiLCJTTkhHMjUiLCJWQ0FOIiwiTFVNIiwiRENOIiwiUzEwMDRBIikpICsgUm90YXRlZEF4aXMoKQoKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGFzdHJvMlsxOjE1XSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGFzdHJvMlsxOjE1XSkgK1JvdGF0ZWRBeGlzKCkKCgojIHJhZGlhbCBnbGlhIHN1YnR5cGluZwpJZGVudHMoc2V1LnEpIDwtICgnc3ViZ3JvdXBzJykKcmcuc3ViLm1hcmtlcnMgPC0gRmluZE1hcmtlcnMoc2V1LnEsIGlkZW50LjEgPSAiUkcxIiwgaWRlbnQuMiA9ICJSRzIiLCBvbmx5LnBvcyA9IEZBTFNFKQp0b3A1LnVwIDwtIHJnLnN1Yi5tYXJrZXJzICU+JSB0b3BfbihuPTEwLCB3dCA9IGF2Z19sb2cyRkMpCnRvcDUuZG93biA8LSByZy5zdWIubWFya2VycyAlPiUgdG9wX24obj0tMTAsIHd0ID0gYXZnX2xvZzJGQykKZnQgPC0gcm93bmFtZXModG9wNS51cCkKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZ0KSArIFJvdGF0ZWRBeGlzKCkKCgpmdCA8LSByb3duYW1lcyh0b3A1LmRvd24pCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZnQsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmdCkgKyBSb3RhdGVkQXhpcygpCgpucGMubWFya2VycyA8LSBGaW5kTWFya2VycyhzZXUucSwgaWRlbnQuMSA9ICJQcmVjdXJzb3JzIiwgaWRlbnQuMiA9IGMoIkFzdHJvY3l0ZXMyIiwiQXN0cm9jeXRlczEiKSwgb25seS5wb3MgPSBGQUxTRSkKdG9wMTAubnBjIDwtIG5wYy5tYXJrZXJzICU+JSB0b3BfbihuPTEwLCB3dCA9IGF2Z19sb2cyRkMpCm5wYy5tYXJrZXJzIDwtIG5wYy5tYXJrZXJzICU+JSBmaWx0ZXIoYXZnX2xvZzJGQyA+IDApCmRpbShucGMubWFya2VycykKCmZ0IDwtIHJvd25hbWVzKHRvcDEwLm5wYykKIyBjb25zaWRlciBuYW1pbmcgcHJlY3Vyc29ycyBhcyBhc3Ryb2N5dGVzMwpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZnQsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmdCkgKyBSb3RhdGVkQXhpcygpCgoKbnBjLm1hcmtlcnMucmcgPC0gRmluZE1hcmtlcnMoc2V1LnEsIGlkZW50LjEgPSAiUHJlY3Vyc29ycyIsIGlkZW50LjIgPSBjKCJSRzIiLCJSRzEiKSwgb25seS5wb3MgPSBUUlVFKQp0b3AxMC5ucGMgPC0gbnBjLm1hcmtlcnMucmcgJT4lIHRvcF9uKG49MTAsIHd0ID0gYXZnX2xvZzJGQykKCmZ0IDwtIHJvd25hbWVzKHRvcDEwLm5wYykKIyBjb25zaWRlciBuYW1pbmcgcHJlY3Vyc29ycyBhcyBhc3Ryb2N5dGVzMwpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZnQsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmdCkgKyBSb3RhdGVkQXhpcygpCgojIyB0aGVzZSBoYXZlIG1vcmUgZGlmZmVyZW5jZXMgZnJvbSBSRyB0aGFuIEFzdHJvY3l0ZXMKCgpgYGAKCkFkZCBzdWJ0eXBlIGdlbmUgaWRzCgpgYGB7cn0KCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuMicpCgpjbHVzdGVyLmlkcyA8LSBjKCJBc3Ryb2N5dGVzLVBMQ0cyIiwiQXN0cm9jeXRlcy1ETkMiLCJBc3Ryb2N5dGVzLUlHRkJQMiIsCiAgICAgICAgICAgICAgICAgIlJHMS1DREtOMUMiLCJSRzItVFlSUDEiLCJFbmRvdGhlbGlhbCIpCiNjbHVzdGVyLmlkcyA8LSBjKCJBc3Ryb2N5dGVzMSIsIkFzdHJvY3l0ZXMyIiwiUHJlY3Vyc29ycyhBc3Ryb2N5dGVzKSIsIlJHMSIsIlJHMiIsIkVuZG90aGVsaWFsIikKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1LnEpCnNldS5xIDwtIFJlbmFtZUlkZW50cyhzZXUucSwgY2x1c3Rlci5pZHMpCnNldS5xJENlbGxfdHlwZXMgPC0gSWRlbnRzKHNldS5xKQoKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ0NlbGxfdHlwZXMnLCByZXBlbCA9IFRSVUUpCgoKc2F2ZVJEUyhzZXUucSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMUxhYmxlZFNldTMwMTEwMjAyMi5SRFMiKQoKIyBsYWJlbCB3aXRoIG1haW4gY2VsbCB0eXBlIGdyb3VwcyAKCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuMicpCgpjbHVzdGVyLmlkcyA8LSBjKCJBc3Ryb2N5dGVzIiwiQXN0cm9jeXRlcyIsIkFzdHJvY3l0ZXMiLAogICAgICAgICAgICAgICAgICJSRyIsIlJHIiwiRW5kb3RoZWxpYWwiKQojY2x1c3Rlci5pZHMgPC0gYygiQXN0cm9jeXRlczEiLCJBc3Ryb2N5dGVzMiIsIlByZWN1cnNvcnMoQXN0cm9jeXRlcykiLCJSRzEiLCJSRzIiLCJFbmRvdGhlbGlhbCIpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRDZWxsX1R5cGUgPC0gSWRlbnRzKHNldS5xKQoKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ0NlbGxfVHlwZScsIHJlcGVsID0gVFJVRSkKCgpzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL0dsaWExTGFibGVkU2V1MzAxMTAyMDIyLlJEUyIpCgoKCgpgYGAKCgpMYWJlbCBtYWluIGNlbGwgdHlwZXMgClJlcyAwLjgJcHJlZGljaXRvbnMKMAlhc3RybwoxCUVuZG8vUkcvQXN0cm8KMglBc3Ryb2N5dGUKMwlSRy9FbmRvL0FzdHJvCjQJQXN0cm8KNQlBc3Rybwo2CUVwaXRoZWxhLCBlbmRvLCBhc3RybywgUkcKNwlBc3Rybwo4CUFzdHJvCjkJQXN0cm8vUkcvRW5kbwoxMAlBc3Ryby9SRwoxMQlBc3Ryby9OZXVyb25zCjEyCUVuZG8vUkcuIAoKCmBgYHtyfQoKI0RpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuOCcpCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuOCcKCmNsdXN0ZXIuaWRzIDwtIGMoIkFzdHJvY3l0ZXMiLCJBc3Ryb2N5dGVzLUVuZG8tUkciLCJBc3Ryb2N5dGVzIiwKICAgICAgICAgICAgICAgICAiUkctRW5kby1Bc3RybyIsIkFzdHJvIiwiQXN0cm8iLCJFcGktRW5kby1Bc3Ryby1SRyIsCiAgICAgICAgICAgICAgICAgIkFzdHJvIiwiQXN0cm8iLCJBc3Ryby1SRy1FbmRvIiwiQXN0cm8tUkciLCJBc3Ryby1OZXVyb25zIiwiRW5kby1SRyIpCgpjbHVzdGVyLmlkcyA8LSBjKCJBc3Ryb2N5dGVzIiwiQXN0cm9jeXRlcyIsIkFzdHJvY3l0ZXMiLAogICAgICAgICAgICAgICAgICJBc3Ryb2N5dGVzIiwiQXN0cm9jeXRlcyIsIkFzdHJvY3l0ZXMiLCJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgIkFzdHJvY3l0ZXMiLCJBc3Ryb2N5dGVzIiwiUmFkaWFsIEdsaWEiLCJSYWRpYWwgR2xpYSIsIkFzdHJvY3l0ZXMiLCJFbmRvdGhlbGlhbCIpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRDZWxsX1R5cGUgPC0gSWRlbnRzKHNldS5xKQoKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ0NlbGxfVHlwZScsIHJlcGVsID0gVFJVRSkKCgpzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL0dsaWExTGFibGVkU2V1MzAxMTAyMDIyLlJEUyIpCgoKCmBgYAoKYGBge3J9CnRhYmxlKHNldS5xJENlbGxfVHlwZSkKCmBgYAoKCgpRdWljayBjaGVjayB0aGUgR2xpYWwyIAoKYGBge3J9CgojIGV4cGxvcmUgZmlsdGVyaW5nCnNldSA8LSBHbGlhMgpzZXUKIyAKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSwgbmNvbCA9IDMpCgpWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiksIHkubWF4ID0gMTAwMCkKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIpLCB5Lm1heCA9IDUwMCkKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5Db3VudF9STkEiKSwgeS5tYXggPSAyMDAwKQoKIyBmaWx0ZXIgbW9yZSBjZWxscwoKc2V1LmZ0IDwtIHN1YnNldChzZXUsIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDI1MCAmIG5Db3VudF9STkEgPiAyNTAgJiBuQ291bnRfUk5BIDwgMTAwMDApIApzZXUuZnQKClZsblBsb3Qoc2V1LmZ0LCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiKSwgeS5tYXggPSAyMDAwKQoKYGBgCmBgYHtyfQoKVmxuUGxvdChzZXUuZnQuZ2xpYSwgZmVhdHVyZXMgPSBjKCJDRDQ0IiwiUzEwMEIiLCJJVEdCMSIpKQpWbG5QbG90KHNldS5mdCwgZmVhdHVyZXMgPSBjKCJDRDQ0IiwiUzEwMEIiLCJJVEdCMSIpLCBncm91cC5ieSA9ICdvcmlnLmlkZW50JykKIyBib3RoIGdsaWEgcG9wdWxhdGlvbnMgYXJlIHNpbWlsYXIKCgpgYGAKCkxldmVscyBzZWVtIHNpbWlsYXIgaW4gR2xpYTEgYW5kIEdsaWEyCgpSZW1vdmUgZG91YmxldHMgYW5kIHN0YXJ0IHRvIHByb2Nlc3MgR2xpYSAyCgpgYGB7cn0KCnN1cHByZXNzTWVzc2FnZXMocmVxdWlyZShEb3VibGV0RmluZGVyKSkKCiMgZmlsdGVyaW5nIG91dCBNQUxBVDEgYW5kIG1pdG9jaG9uZHJpYWwgZ2VuZXMKCnNldS5mdCA8LSBzZXUuZnRbIWdyZXBsKCJNQUxBVDEiLCByb3duYW1lcyhzZXUuZnQpKSwgXQpzZXUuZnQgPC0gc2V1LmZ0WyFncmVwbCgiXk1ULSIsIHJvd25hbWVzKHNldS5mdCkpLCBdCgojIGxpa2UgaW4gdGhlIHR1dG9yaWFsIEknbSBmb2xsb3dpbmcgTUFMQVQxIGlzIHRoZSB0b3AgbW9zdCBleHByZXNzZWQgZ2VuZS4gIFRoZSB0b3AgZ2VuZXMgYXJlIGEgbG90IG9mIE1UIGFuZCBSaWJvc29tYWwgZ2VuZXMKCnNldS5mdFtbInBlcmNlbnQucmIiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoc2V1LmZ0LCBwYXR0ZXJuID0gIl5SUCIpCgpzZXUuZCA9IE5vcm1hbGl6ZURhdGEoc2V1LmZ0KQpzZXUuZCA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldS5kLCB2ZXJib3NlID0gRikKc2V1LmQgPSBTY2FsZURhdGEoc2V1LmQsIHZhcnMudG8ucmVncmVzcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJwZXJjZW50Lm10IiksCiAgICB2ZXJib3NlID0gRikKc2V1LmQgPSBSdW5QQ0Eoc2V1LmQsIHZlcmJvc2UgPSBGLCBucGNzID0gMTUpCnNldS5kID0gUnVuVU1BUChzZXUuZCwgZGltcyA9IDE6MTAsIHZlcmJvc2UgPSBGKQoKbkV4cCA8LSByb3VuZChuY29sKHNldS5kKSAqIDAuMDgpICAjIGV4cGVjdCBtb3JlIGRvdWJsZXRzIGJlY2F1c2UgdGhlcmUgaXMgYSBsb3QgbW9yZSBjZWxscwpzZXUuZCA8LSBkb3VibGV0RmluZGVyX3YzKHNldS5kLCBwTiA9IDAuMjUsIHBLID0gMC4wOSwgbkV4cCA9IG5FeHAsIFBDcyA9IDE6MTApCiMgdGhlIG1lbW9yeSBsaW1pdCBpcyByZWFjaGVkIGhlcmUgLSBJIGNvdWxkIHJ1biBvbiBjb21wdXRlIGNhbmFkYQojIEZvciBub3cgSSdsbCBkb3duc2FtcGxlCiMgdGhpcyB3b3JrcwoKIyBuYW1lIG9mIHRoZSBERiBwcmVkaWN0aW9uIGNhbiBjaGFuZ2UsIHNvIGV4dHJhY3QgdGhlIGNvcnJlY3QgY29sdW1uIG5hbWUuCkRGLm5hbWUgPSBjb2xuYW1lcyhzZXUuZEBtZXRhLmRhdGEpW2dyZXBsKCJERi5jbGFzc2lmaWNhdGlvbiIsIGNvbG5hbWVzKHNldS5kQG1ldGEuZGF0YSkpXQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChuY29sID0gMiwgRGltUGxvdChzZXUuZCwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpICsgTm9BeGVzKCksCiAgICBEaW1QbG90KHNldS5kLCBncm91cC5ieSA9IERGLm5hbWUpICsgTm9BeGVzKCkpCgpWbG5QbG90KHNldS5kLCBmZWF0dXJlcyA9ICJuRmVhdHVyZV9STkEiLCBncm91cC5ieSA9IERGLm5hbWUsIHB0LnNpemUgPSAwLjEpCgpgYGAKCgpgYGB7cn0KCnNldS5kIDwtIHNldS5kWywgc2V1LmRAbWV0YS5kYXRhWywgREYubmFtZV09PSAiU2luZ2xldCJdCmRpbShzZXUuZCkKZGltKHNldS5mdCkKCgoKYGBgCgpDbHVzdGVyIAoKYGBge3J9CnNldSA8LSBOb3JtYWxpemVEYXRhKHNldS5kLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCkKc2V1IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldSwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQpzZXUgPC0gU2NhbGVEYXRhKHNldSkKc2V1IDwtIFJ1blBDQShzZXUpCnNldSA8LSBSdW5VTUFQKHNldSwgcmVkdWN0aW9uID0gInBjYSIsIG4ubmVpZ2hib3JzID0gMjUsIGRpbXMgPSAxOjMwKQpEaW1QbG90KHNldSwgcmVkdWN0aW9uID0gInVtYXAiKQoKc2V1LnEgPC0gRmluZE5laWdoYm9ycyhzZXUsIGRpbXMgPSAxOjI1LCBrLnBhcmFtID0gMjUpCnNldS5xIDwtIEZpbmRDbHVzdGVycyhzZXUucSwgcmVzb2x1dGlvbiA9IGMoMCwwLjA1LDAuMiwwLjQsMC41LDAuNiwwLjgpKQpsaWJyYXJ5KGNsdXN0cmVlKQoKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjA1JykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjEnKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuMicpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC40JykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuOCcpCmNsdXN0cmVlKHNldS5xKQojIDAuNCBpcyBsaWtlbHkgdGhlIGJlc3QgYW5ub3RhdGUgc3ViZ3JvdXBzCgoKYGBgCgpQcmVkaWN0IGNlbGwgdHlwZXMgCgoKYGBge3J9CgojIFNOQ0EgYW5kIGNvbnRyb2wgbWlkYnJhaW4gb3JnYW5vaWRzIDE2NSBkYXlzIGluIGN1bHR1cmUKTUJPIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FTVDIzX0JyYWluQ29tbS9NQk9jbHVzdGVyc19uYW1lczI5MDcyMDIxLnJkcyIpCgojIE1pZGJyYWluICBBSVcwMDIgMTIwIGRheXMgaW4gY3VsdHVyZQpBSVdNQk8gPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvQUlXdHJpbzEyMGRheXMvTU9pbnRlZ3JhdGVkQ2x1c3RlcksxMjNyZXMwLjgubmFtZXNfbm92MTZfMjAyMSIpCgojIE1pZGJyYWluIEFJVzAwMiA2MCBkYXlzIGluIGN1bHR1cmUKCkFJVzYwIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FJV3RyaW82MGRheXMvQVdJMDAyUGFya2luS09QaW5rS082MGRheXNfbGFiZWxzXzE0MDUyMDIyLnJkcyIpCgoKI2ZpcnN0IHByZWRpY3Qgd2l0aCB0aGUgTUJPIGRhdGEKSWRlbnRzKE1CTykgPC0gImNsdXN0ZXJfbGFiZWxzIgpEZWZhdWx0QXNzYXkoTUJPKSA8LSAiUk5BIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwpwcmludCgiZmluZGluZyByZWZlcmVuY2UgYW5jaG9ycyIpCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBNQk8gLHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gTUJPJGNsdXN0ZXJfbGFiZWxzKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRNQk9BU1QyMy5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnTUJPQVNUMjMucHJlZCcsIGxhYmVsID0gVFJVRSkKCiMgc2VlIGhvdyBhY2N1cmF0ZSB0aGUgcHJlZGljdGlvbnMgYXJlCnNldS5xJHByZWRpY3RlZC5pZCA8LSBpZmVsc2Uoc2V1LnEkcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjk1LCBzZXUucSRwcmVkaWN0ZWQuaWQsICJOb25lIikKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkTUJPQVNUMjMudGhyZXNoIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAncHJlZGljdGVkLmlkJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRNQk9BU1QyMy5wcmVkKQp0YWJsZShzZXUucSRNQk9BU1QyMy50aHJlc2gpCgoKIAojIyBjaGVjayB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHByZWRpY3RlZCBpbiBlYWNoIGNsdXN0ZXIKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjQsIHNldS5xJE1CT0FTVDIzLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQoKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgY2x1c3RlcnMgZG9uJ3QgYnJlYWsgdXAgYnkgdGhlIHByZWRpY3RlZCBjZWxsIHR5cGVzCgojIyMjIyMjIyMjIyMgYW5vdGhlciBwcmVkaWN0aW9ucyBub3cgdXNpbmcgdGhlIEFJVyBvcmdhbm9pZHMKCklkZW50cyhBSVdNQk8pIDwtICJyZXMwOG5hbWVzIgpEZWZhdWx0QXNzYXkoQUlXTUJPKSA8LSAiUk5BIgoKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IEFJV01CTyAscXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBBSVdNQk8kcmVzMDhuYW1lcykKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCiMgYWRkIG5ldyBkYXRhc2xvdCBmb3IgTUJPIHByZWRpY3RlZCBJRCB0byBtYWtlIHRoZSBuZXh0IHByZWRpY3Rpb24Kc2V1LnEkQUlXMTIwLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdBSVcxMjAucHJlZCcsIGxhYmVsID0gVFJVRSkKIAojIyBjaGVjayB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHByZWRpY3RlZCBpbiBlYWNoIGNsdXN0ZXIKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjQsIHNldS5xJE1CT0FJVy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgc2VlIGhvdyBhY2N1cmF0ZSB0aGUgcHJlZGljdGlvbnMgYXJlCnNldS5xJHByZWRpY3RlZC5pZCA8LSBpZmVsc2Uoc2V1LnEkcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjk1LCBzZXUucSRwcmVkaWN0ZWQuaWQsICJOb25lIikKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQUlXMTIwLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0FJVzEyMC50aHJlc2gnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJEFJVzEyMC5wcmVkKQp0YWJsZShzZXUucSRBSVcxMjAudGhyZXNoKQoKIyB0aGUgcHJlZGljdGVkIGNlbGwgdHlwZXMgbWFrZSBtb3JlIHNlbnNlIGZyb20gdGhlIEFJVzAwMiBvcmdhbm9pZAojIG5vdyBwcmVkaWN0IHdpdGggdGhlIEFJVzAwMiA2MCBkYXlzIG9yZ2Fub2lkCgpJZGVudHMoQUlXNjApIDwtICJjbHVzdGVyLmlkcyIKRGVmYXVsdEFzc2F5KEFJVzYwKSA8LSAiUk5BIgoKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IEFJVzYwLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IEFJVzYwJGNsdXN0ZXIuaWRzKSAKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCiMgYWRkIG5ldyBkYXRhc2xvdCBmb3IgTUJPIHByZWRpY3RlZCBJRCB0byBtYWtlIHRoZSBuZXh0IHByZWRpY3Rpb24Kc2V1LnEkQUlXNjAucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0FJVzYwLnByZWQnLCBsYWJlbCA9IFRSVUUpCiAKIyMgY2hlY2sgdGhlIHByb3BvcnRpb24gb2YgY2VsbCB0eXBlcyBwcmVkaWN0ZWQgaW4gZWFjaCBjbHVzdGVyCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC40LCBzZXUucSRBSVc2MC5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKCiMgdHJ5IGJhciBjaGFydApnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpCgojIHNlZSBob3cgYWNjdXJhdGUgdGhlIHByZWRpY3Rpb25zIGFyZQpzZXUucSRwcmVkaWN0ZWQuaWQgPC0gaWZlbHNlKHNldS5xJHByZWRpY3Rpb24uc2NvcmUubWF4ID4gMC45NSwgc2V1LnEkcHJlZGljdGVkLmlkLCAiTm9uZSIpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEFJVzYwLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0FJVzYwLnRocmVzaCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkQUlXNjAucHJlZCkKdGFibGUoc2V1LnEkQUlXNjAudGhyZXNoKQoKCgojIG1vc3Qgb2YgdGhlIGNlbGxzIGFyZSBwcmVkaWN0ZWQgYXMgTlBDcyBpbiBtYW55IHBvcHVsYXRpb25zCgoKCmBgYAoKYGBge3J9CiMgc2F2ZSB3aXRoIHByZWRpY3Rpb25zIHNvIGZhcgojc2F2ZVJEUyhzZXUucSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMkxhYmxlZFNldTAzMTAyMDIyLlJEUyIpCgpzZXUucSA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTJMYWJsZWRTZXUwMzEwMjAyMi5SRFMiKQoKCmBgYAoKClNlZSBob3cgbWFueSBjZWxscyBhcmUgcHJlZGljdGVkIGFzIGFzdHJvY3l0ZXMgd2l0aCB0aGUgdGhyZXNob2xkCgpgYGB7cn0KCkRBc3VidHlwZXMgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvTWFjb3Nrb19EYXRhL0RBc3ViZ3JvdXBzX3Byb2Nlc3NlZC5SZHMiKQoKSWRlbnRzKGFzdHJvLnJlZikgPC0gIkNlbGxfU3VidHlwZSIKRGVmYXVsdEFzc2F5KGFzdHJvLnJlZikgPC0gIlJOQSIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKcHJpbnQoImZpbmRpbmcgcmVmZXJlbmNlIGFuY2hvcnMiKQphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gREFzdWJ0eXBlcyAscXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjApCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBhc3Ryby5yZWYkQ2VsbF9TdWJ0eXBlLCBrLndlaWdodCA9IDEwKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRhc3Ryby5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnYXN0cm8ucHJlZCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkYXN0cm8ucHJlZCkKCnNldS5xJHByZWRpY3RlZC5pZCA8LSBpZmVsc2Uoc2V1LnEkcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjk1LCBzZXUucSRwcmVkaWN0ZWQuaWQsICJub25lIikKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkYXN0cm8ucHJlZC50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdhc3Ryby5wcmVkLnRocmVzaCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkYXN0cm8ucHJlZC50aHJlc2gpCgoKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJGFzdHJvLnByZWQudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmFzdHJvIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYXN0cm8gPC0gdG9wLnByZWQuYXN0cm9bb3JkZXIodG9wLnByZWQuYXN0cm8kVmFyMSwtdG9wLnByZWQuYXN0cm8kRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFzdHJvKSA8LSBOVUxMCgoKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJGFzdHJvLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuYXN0cm8gPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5hc3RybyA8LSB0b3AucHJlZC5hc3Ryb1tvcmRlcih0b3AucHJlZC5hc3RybyRWYXIxLC10b3AucHJlZC5hc3RybyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYXN0cm8pIDwtIE5VTEwKCiMgYSBsb3Qgb2YgdGhlc2UgY2VsbHMgYXJlIGFsc28gZ2V0dGluZyBsYWJlbGxlZCBhcyBhc3Ryb2N5dGVzCgoKYGBgCgpEbyB0aGVzZSBnZXQgbGFiZWxsZWQgYXMgREEgbmV1cm9ucyB0b28/Pz8KCmBgYHtyfQpzZXUucSA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTJMYWJsZWRTZXUwMzEwMjAyMi5SRFMiKQoKYGBgCgoKCmBgYHtyfQoKREFzdWJ0eXBlcyA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9NYWNvc2tvX0RhdGEvREFzdWJncm91cHNfcHJvY2Vzc2VkLlJkcyIpCklkZW50cyhEQXN1YnR5cGVzKSA8LSAiQ2VsbF9TdWJ0eXBlIgpkYS5yZWYgPC0gc3Vic2V0KERBc3VidHlwZXMsIGRvd25zYW1wbGUgPSA1MDApCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCnByaW50KCJmaW5kaW5nIHJlZmVyZW5jZSBhbmNob3JzIikKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IGRhLnJlZiwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjApCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBkYS5yZWYkQ2VsbF9TdWJ0eXBlLCBrLndlaWdodCA9IDEwKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRkYS5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnZGEucHJlZCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkZGEucHJlZCkKCnNldS5xJHByZWRpY3RlZC5pZCA8LSBpZmVsc2Uoc2V1LnEkcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjk1LCBzZXUucSRwcmVkaWN0ZWQuaWQsICJub25lIikKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkZGEucHJlZC50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdkYS5wcmVkLnRocmVzaCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkZGEucHJlZC50aHJlc2gpCgoKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJGRhLnByZWQudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmFzdHJvIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYXN0cm8gPC0gdG9wLnByZWQuYXN0cm9bb3JkZXIodG9wLnByZWQuYXN0cm8kVmFyMSwtdG9wLnByZWQuZGEkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFzdHJvKSA8LSBOVUxMCgoKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJGFzdHJvLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuYXN0cm8gPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5hc3RybyA8LSB0b3AucHJlZC5hc3Ryb1tvcmRlcih0b3AucHJlZC5hc3RybyRWYXIxLC10b3AucHJlZC5hc3RybyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYXN0cm8pIDwtIE5VTEwKCgojIGFmdGVyIHRocmVzaG9sZGluZyB2ZXJ5IGZldyBjZWxscyBhcmUgcHJlZGljdGVkIGFzIG5ldXJvbnMKCgoKYGBgCgp7cmVkaWN0ZSB3aXRoIHRoZSBicmFpbiBzY1JOQXNlcQoKYGBge3J9CgpwYXRod2F5IDwtICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvIgpzZXUucSA8LSByZWFkUkRTKHBhc3RlKHBhdGh3YXksIkdsaWEyTGFibGVkU2V1MDMxMDIwMjIuUkRTIixzZXAgPSAiIikpCgojIG1pZGJyYWluIGFuZCBzdHJpYXR1bQoKc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9CaGFkdXJpX3dob2xlQnJhaW4vQmhhZHVyaV9taWRicmFpbl9zdHJpYXR1bS5SRFMiKQoKSWRlbnRzKHNldS5yKSA8LSAiY2VsbF9jbHVzdGVyIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1LnIsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gc2V1LnIkY2VsbF9jbHVzdGVyKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQgPC0gSWRlbnRzKHNldS5xKQpwcmludCh0YWJsZShzZXUucSRCaGEubWlkLnN0cmkucHJlZCkpCgoKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgIm5vbmUiKQpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5taWQucHJlZC50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEubWlkLnByZWQudGhyZXNoJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRCaGEubWlkLnByZWQudGhyZXNoKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEubWlkLnByZWQudGhyZXNoJykKCgojIHJlYWQgaW4gdGhlIHJlZmVyZW5jZSBkYXRhc2V0CiMgd2hvbGUgYnJhaW4gQmhhZHVyaSBkb3duIHNhbXBsZWQKc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9CaGFkdXJpX3dob2xlQnJhaW4vQmhhZHVyaV9kb3duc2FtcGxlLlJEUyIpCgpJZGVudHMoc2V1LnIpIDwtICJjZWxsX2NsdXN0ZXIiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRjZWxsX2NsdXN0ZXIpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRCaGEucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJEJoYS5wcmVkKSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQmhhLnByZWQnKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdzdWJncm91cHMnKQoKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgIm5vbmUiKQpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5taWQucHJlZC50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEucHJlZC50aHJlc2gnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJEJoYS5taWQucHJlZC50aHJlc2gpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5wcmVkLnRocmVzaCcpCgoKYGBgCgoKCkNvbXBhcmUgcHJlZGljdGlvbnMgLSBtYWtlIGEgcHJlZGljdGlvbnMgdGFibGUKCmBgYHtyfQoKIyBBSVcwMDIgMTIwIGRheXMgcHJlZGljdGlvbnMgLSB0YWtlIHRoZSB0aHJlc2hvbGRlZCBvcHRpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRBSVcxMjAudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzEyMCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKSA8LSBOVUxMCmRmLnRvcC5haXcxMjAkSSA8LSByb3cubmFtZXMoZGYudG9wLmFpdzEyMCkKCiMgQUlXMDAyIDYwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJEFJVzYwLnRocmVzaCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCA8LWFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3NjAgPC0gdG9wLnByZWQuY2VsbHR5cGUuQUlXNjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFpdzYwKSA8LSBOVUxMCmRmLnRvcC5haXc2MCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3NjApCgoKIyBBU1QyMyAxNjUgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkTUJPQVNUMjMudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuQVNUMjMgPC0gdG9wLnByZWQuY2VsbHR5cGUuQVNUMjNbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQVNUMjMkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLkFTVDIzKSA8LSBOVUxMCmRmLnRvcC5BU1QyMyRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQVNUMjMpCgojIGFkZCB0aGUgdGhyZXNob2xkIEFzdHJvIHByZWRpY3Rpb25zIAp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkYXN0cm8ucHJlZC50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuYXN0cm8gPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5hc3RybyA8LSB0b3AucHJlZC5jZWxsdHlwZS5hc3Ryb1tvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5hc3RybyRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5hc3RybyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYXN0cm8pIDwtIE5VTEwKZGYudG9wLmFzdHJvJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5hc3RybykKCiMgYWRkIHRoZSBuZXVyb25zIHByZWRpY3Rpb25zIAp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkZGEucHJlZC50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuZGEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5kYSA8LSB0b3AucHJlZC5jZWxsdHlwZS5kYVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5kYSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5kYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuZGEpIDwtIE5VTEwKZGYudG9wLmRhJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5kYSkKCgojIyMgYWRkIGluIHRoZSBwcmVkaWN0aW9uIGZyb20gYnJhaW4gZGF0YSBCaGFkdXJpIG1pZGJyYWluIGFuZCBzdHJpYXR1bQp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkQmhhLm1pZC5wcmVkLnRocmVzaCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5CaGEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5CaGEgPC0gdG9wLnByZWQuY2VsbHR5cGUuQmhhW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkJoYSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5CaGEkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLkJoYSkgPC0gTlVMTApkZi50b3AuQmhhJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5CaGEpCgojIyB0aGVzZSBhcmUgY2FsY3VsYXRlZCBiZWxvdwojIyMgYWRkIGluIHRoZSBwcmVkaWN0aW9uIGZyb20gYnJhaW4gd2hvbGUgYnJhaW4gZGF0YSBCaGFkdXJpIG1pZGJyYWluIGRvd24gc2FtcGxlZAp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkQmhhLnByZWQudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkJoYTEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5CaGExIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkJoYTFbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQmhhJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkJoYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQmhhMSkgPC0gTlVMTApkZi50b3AuQmhhMSRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQmhhMSkKCnByZWQudGFibGUgPC0gbWVyZ2UoZGYudG9wLkFTVDIzLCBkZi50b3AuYWl3NjAsIGJ5ID0gJ0knLCBhbGwgPSBUUlVFKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5haXcxMjAsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGExLCBieSA9ICdJJykKcHJlZC50YWJsZQoKCgpgYGAKClByZWRpY3RlZCBjbHVzdGVyIGFubm90YXRpb25zCjAJVW5rbm93bi8gTlBDCjEJUkcKMglhc3RybwozCVJHCjQJbmV1cm9ucwo1CVJHCgpQcmVkaWN0IGZyb20gZGV2ZWxvcGluZyBjb3J0ZXggCgpgYGB7cn0KCnBhdGh3YXkgPC0gIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy8iCnNldS5xIDwtIHJlYWRSRFMocGFzdGUocGF0aHdheSwiR2xpYTJMYWJsZWRTZXUwMzEwMjAyMi5SRFMiLHNlcCA9ICIiKSkKCnNldS5yIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1B1YmxpY0RhdGEvTm93YWtvd3NraV9kZXZfY29ydGV4dC5SRFMiKQpjb2xuYW1lcyhzZXUuckBtZXRhLmRhdGEpCgpJZGVudHMoc2V1LnIpIDwtICJXR0NOQWNsdXN0ZXIiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRXR0NOQWNsdXN0ZXIpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRkZXYuY29ydGV4LnByZWQgPC0gSWRlbnRzKHNldS5xKQpwcmludCh0YWJsZShzZXUucSRkZXYuY29ydGV4LnByZWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdkZXYuY29ydGV4LnByZWQnKQoKCiMgYWRkIHRoZSB0aHJlc2hvbGQgCnNldS5xJHByZWRpY3RlZC5pZCA8LSBpZmVsc2Uoc2V1LnEkcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjgsIHNldS5xJHByZWRpY3RlZC5pZCwgIm5vbmUiKQpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJGRldi5jb3J0ZXgucHJlZC50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdkZXYuY29ydGV4LnByZWQudGhyZXNoJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRkZXYuY29ydGV4LnByZWQudGhyZXNoKQoKCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRkZXYuY29ydGV4LnByZWQudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AgPC0gdG9wLnByZWQuY2VsbHR5cGVbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wKSA8LSBOVUxMCmRmLnRvcCRJIDwtIHJvdy5uYW1lcyhkZi50b3ApCgoKIyBhbG1vc3QgZXZlcnl0aGluZyBpcyBwcmVkaWN0ZWQgYXMgJ25vbmUnIHdoZW4gdGhlc2hvbGQgaXMgLjk1IGNoZWNrIHByZWRpY3Rpb25zIHdpdGhvdXQgdGhyZXNob2xkIGFuZCB0aGVyZSBhcmUgbWFueSAKIyBydW4gYWdhaW4gd2l0aCBsb3dlciB0aHJlc2hvbGQgMC44Cgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkZGV2LmNvcnRleC5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDQsIEZyZXEpKQpkZi50b3AgPC0gdG9wLnByZWQuY2VsbHR5cGVbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wKSA8LSBOVUxMCmRmLnRvcCRJIDwtIHJvdy5uYW1lcyhkZi50b3ApCgpkZi50b3AuZnQgPC0gZGYudG9wICU+JSBmaWx0ZXIoRnJlcSA+IDApCgoKYGBgCgoKVGhlIGRldiBjb3J0ZXggZG9lc24ndCBwcmVkaWN0IEdsaWEgMiB3ZWxsClRyeSB0aGUgZGV2ZWxvcGluZyBmb3JlYnJhaW4KCmBgYHtyfQpzZXUuciA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QdWJsaWNEYXRhL0thcm9saW5za2lfRGV2Rm9yZWJyYWluX2Rvd25zYW1wbGVfTGV2ZWwxLlJEUyIpCmNvbG5hbWVzKHNldS5yQG1ldGEuZGF0YSkKRGVmYXVsdEFzc2F5KHNldS5yKSA8LSAnUk5BJwoKIyBjbHVzdGVycyBoYXMgc3ViZ3JvdXBzCiMgbGV2ZWxzIGFyZSBtYWluIGNlbGwgZ3JvdXBzIApJZGVudHMoc2V1LnIpIDwtICJMZXZlbDEiCgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1LnIsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gc2V1LnIkTGV2ZWwxKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkZmIucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJGZiLnByZWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdmYi5wcmVkJykKCgojIGFkZCB0aGUgdGhyZXNob2xkIApzZXUucSRwcmVkaWN0ZWQuaWQgPC0gaWZlbHNlKHNldS5xJHByZWRpY3Rpb24uc2NvcmUubWF4ID4gMC44MCwgc2V1LnEkcHJlZGljdGVkLmlkLCAibm9uZSIpCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkZmIudGhyZXNoIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnZmIudGhyZXNoJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRmYi50aHJlc2gpCgojIG1ha2UgdGhlIHRhYmxlcyAKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJGZiLnRocmVzaCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wIDwtIHRvcC5wcmVkLmNlbGx0eXBlW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcCkgPC0gTlVMTApkZi50b3AkSSA8LSByb3cubmFtZXMoZGYudG9wKQoKI1ZMTUMgaXMgdmFzY3VsYXIgYW5kIGxlcHRvbWVuaW5nZXMKCiMgYWxtb3N0IGV2ZXJ5dGhpbmcgaXMgcHJlZGljdGVkIGFzICdub25lJyB3aGVuIHRoZXNob2xkIGlzIC45NSAKIyBydW4gYWdhaW4gd2l0aCBsb3dlciB0aHJlc2hvbGQgMC44IGFuZCBtYW55IGFyZSBwcmVkaWN0ZWQgYXMgUkcKCiMgdHJ5IHByZWRpY3Rpbmcgd2l0aCB0aGUgY2x1c3RlciBsYWJlbHMgCiMgbGF0ZXIgSSBjYW4gc3Vic2V0IGNlbGwgdHlwZXMgZm9yIHRoZSByZWZlcmVuY2UgZGF0YQoKSWRlbnRzKHNldS5yKSA8LSAiQ2x1c3RlcnMiCgphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1LnIsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gc2V1LnIkQ2x1c3RlcnMpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRmYi5zdWIucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJGZiLnN1Yi5wcmVkKSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnZmIuc3ViLnByZWQnKQoKCiMgYWRkIHRoZSB0aHJlc2hvbGQgCnNldS5xJHByZWRpY3RlZC5pZCA8LSBpZmVsc2Uoc2V1LnEkcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjgwLCBzZXUucSRwcmVkaWN0ZWQuaWQsICJub25lIikKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRmYi5zdWIudGhyZXNoIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnZmIuc3ViLnRocmVzaCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkZmIuc3ViLnRocmVzaCkKCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRmYi5zdWIudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDQsIEZyZXEpKQpkZi50b3AgPC0gdG9wLnByZWQuY2VsbHR5cGVbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wKSA8LSBOVUxMCmRmLnRvcCRJIDwtIHJvdy5uYW1lcyhkZi50b3ApCgpkZi50b3AuZnQgPC0gZGYudG9wICU+JSBmaWx0ZXIoRnJlcSA+IDApCgpgYGAKClByZWRpY3Rpb25zIHdpdGggdGhyZXNob2xkczoKCjAgLSBSRwoxIC0gUkcKMiAtIG5vbmUKMyAtIFJHCjQgLSBOZXVyYWwgcHJlY3Vyc29yCjUgLSBWTE1DICh2YXNjdWxhciBhbmQgbGVwdG9tZW5pbmdlcykKCkxvb2sgYXQgZ2VuZSBsaXN0cyB3aXRoIGtub3duIG1hcmtlcnMKCmBgYHtyfQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC4yJwoKIyBtYW55IGNlbGwgdHlwZXMgbGlzdApmZWF0dXJlX2xpc3QgPSBjKCJNS0k2NyIsIlNPWDIiLCJQT1U1RjEiLCJETFgyIiwiUEFYNiIsIlNPWDkiLCJIRVMxIiwiTkVTIiwiUkJGT1gzIiwiTUFQMiIsIk5DQU0xIiwiQ0QyNCIsIkdSSUEyIiwiR1JJTjJCIiwiR0FCQlIxIiwiR0FEMSIsIkdBRDIiLCJHQUJSQTEiLCJHQUJSQjIiLCJUSCIsIkFMREgxQTEiLCJMTVgxQiIsIk5SNEEyIiwiQ09SSU4iLCJDQUxCMSIsIktDTko2IiwiQ1hDUjQiLCJJVEdBNiIsIlNMQzFBMyIsIkNENDQiLCJBUVA0IiwiUzEwMEIiLCAiUERHRlJBIiwiT0xJRzIiLCJNQlAiLCJDTEROMTEiLCJWSU0iLCJWQ0FNMSIpCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0KSArUm90YXRlZEF4aXMoKQoKIyBEb3BhbWluZXJnaWMgbWFya2VycwpQRF9wb3VsaW4gPSBjKCJUSCIsIlNMQzZBMyIsIlNMQzE4QTIiLCJTT1g2IiwiTkRORiIsIlNOQ0ciLCJBTERIMUExIiwiQ0FMQjEiLCJUQUNSMiIsIlNMQzE3QTYiLCJTTEMzMkExIiwiT1RYMiIsIkdSUCIsIkxQTCIsIkNDSyIsIlZJUCIpCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gUERfcG91bGluLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gUERfcG91bGluKStSb3RhdGVkQXhpcygpCgplYWxyeU5ldXIgPSBjKCJEQ1giLCJORVVST0QxIiwiVEJSMSIpCnByb2xpZmVyYXRpb24gPSBjKCJQQ05BIiwiTUtJNjciKQpuZXVyYWxzdGVtID0gYygiU09YMiIsIk5FUyIsIlBBWDYiLCJNQVNIMSIpCgpmZWF0dXJlX2xpc3QgPC0gYygiRENYIiwiTkVVUk9EMSIsIlRCUjEiLCJQQ05BIiwiTUtJNjciLCJTT1gyIiwiTkVTIiwiUEFYNiIsIk1BU0gxIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCkrUm90YXRlZEF4aXMoKQoKCm1hdF9uZXVyb24gPSBjKCJSQkZPWDMiLCJTWVAiLCJETEc0NSIsIlZBTVAxIiwiVkFNUDIiLCJUVUJCMyIsIlNZVDEiLCJCU04iLCJIT01FUjEiLCJTTEMxN0E2IikgCiMgTmV1TiBpcyBGT1gzIC0gUkJGT1gzCiMgUFNEOTUgYWxzbyBTUC05MCBvciBETEc0CiMgVkdMVVQyIGlzIFNMQzE3QTYKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IG1hdF9uZXVyb24sIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKIyBjbHVzdGVyIDQgYWxzbyBzaG93IG1hdHVyZSBuZXVyb24gbWFya2VycwpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IG1hdF9uZXVyb24pK1JvdGF0ZWRBeGlzKCkKIyBleGNpdGF0b3J5IG5ldXJvbiBtYXJrZXJzCmV4ID0gYygiR1JJQTIiLCJHUklBMSIsIkdSSUE0IiwiR1JJTjEiLCJHUklOMkIiLCJHUklOMkEiLCJHUklOM0EiLCJHUklOMyIsIkdSSVAxIiwiQ0FNSzJBIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGV4LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZXgpK1JvdGF0ZWRBeGlzKCkKIyBpbmhpYml0b3J5IG5ldXJvbiBtYXJrZXJzCmluaCA9IGMoIkdBRDEiLCJHQUQyIiwgIkdBVDEiLCJQVkFMQiIsIkdBQlIyIiwiR0FCUjEiLCJHQlJSMSIsIkdBQlJCMiIsIkdBQlJCMSIsIkdBQlJCMyIsIkdBQlJBNiIsIkdBQlJBMSIsIkdBQlJBNCIsIlRSQUsyIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGluaCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGluaCkrUm90YXRlZEF4aXMoKQojIGNsdXN0ZXIgNCBpcyBtb3JlIGV4Y2l0YXRvcnkgdGhhbiBpbmhiaXRvcnkgYnV0IG5laXRoZXIgbWFya2VyIHNldCBoYXMgbXVjaCBleHByZXNzaW9uIAoKIyMjIGdsaWEgbWFya2VycwptaWNyb2dsaWEgPSBjKCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiKSAgIyBBREdSRTEgaXMgYSBtaWNyb2dsaWEgbWFya2VyIEY0LzgwLCBDRDQ1IGlzIFBUUFJDLCBnZW5lIG5hbWUgSUJBMSBpcyBBSUYxCmFzdG9sZ05QQ3Byb21pY3JvID0gYygiR0ZBUCIsIlMxMDBCIiwiU0xDMUEyIiwiTUJQIiwiU09YMTAiLCJTUFAxIiwiRENYIiwiTkVVUk9EMSIsIlRCUjEiLCJQQ05BIiwiTUtJNjciLCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiKQojIG5vdGUgR0xUMSBpcyBFQUFUMiB3aGljaCBpcyBTTEMxQTIgZ2x1dGF0bWF0ZSB0cmFuc3BvcnRlcgojIGVwaXRoZWxpYWwKZXBpID0gYygiSEVTMSIsIkhFUzUiLCJTT1gyIiwiU09YMTAiLCJORVMiLCJDREgxIiwiTk9UQ0gxIikgIyBlLWNhZGhlcmluIGlzIENESDEKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBhc3RvbGdOUENwcm9taWNybywgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGFzdG9sZ05QQ3Byb21pY3JvKStSb3RhdGVkQXhpcygpCiMgY2x1c3RlciA0IGlzIG1vcmUgZXhjaXRhdG9yeSB0aGFuIGluaGJpdG9yeSBidXQgbmVpdGhlciBtYXJrZXIgc2V0IGhhcyBtdWNoIGV4cHJlc3Npb24gCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBlcGksIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBlcGkpK1JvdGF0ZWRBeGlzKCkKCiMgYWxzbyBhZGQgUmFkaWFsIGdsaWEgbWFya2VyIG92ZXJsYXAgd2l0aCBHbGlhIGFuZCBOZXVyb25zCgpmZWF0dXJlcyA8LSBjKCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiLCAiVklNIiwgIlROQyIsIlBUUFJaMSIsIkZBTTEwN0EiLCJIT1BYIiwiTElGUiIsCiAgICAgICAgICAgICAgIklUR0I1IiwiSUw2U1QiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZXMsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlcykrUm90YXRlZEF4aXMoKQoKIyByYWRpYWwgZ2xpYSBtYXJrZXJzCnJnIDwtIGMoIlZJTSIsIk5FUyIsIlBBWDYiLCJIRVMxIiwiRUFBVDEiLCJOQ0FEMSIsIlNPWDIiLCJGQUJQNyIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSByZywgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IHJnKStSb3RhdGVkQXhpcygpCgojIE5QQyBhbmQgcmFkaWFsIGdsaWEgYXJlIHZlcnkgc2ltaWxhcgoKYGBgCk1hcmtlciBleHByZXNzaW9uIHByZWRpY3Rpb25zCkNsdXN0ZXIgMCAtIHVua25vd24KQ2x1c3RlciAxIC0gUkcKQ2x1c3RlciAyIC0gdW5rbm93bgpDbHVzdGVyIDMgLSBSRwpjbHVzdGVyIDQgLSBpbW1hdHVyZSBuZXVyb25zCkNsdXN0ZXIgNSAtIFJHLCBvcGMKCgpDaGVjayB0aGUgbGV2ZWxzIG9mIFJOQSBpbiBlYWNoIGNsdXN0ZXIgCgpgYGB7cn0KVmxuUGxvdChzZXUucSwgZmVhdHVyZXMgPSAibkZlYXR1cmVfUk5BIikKCmBgYApDbHVzdGVyIDAgYW5kIDIgaGF2ZSBmZXdlciBzZXF1ZW5jZXMgdGhhbiBvdGhlciBncm91cHMgYW5kIHRodXMgbm8gbWFya2VycwpQb3NzaWJseSByZW1vdmUgdGhlc2UgaXMgdGhleSBkb24ndCBjb21lIHVwIHdpdGggc29tZSBtYXJrZXJzCgpGaW5kIGNsdXN0ZXIgbWFya2VycwoKYGBge3J9CklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuMicKQ2x1c3Rlck1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoc2V1LnEsIG9ubHkucG9zID0gVFJVRSkKCnRvcDUgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuPTUsIHd0ID0gYXZnX2xvZzJGQykKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IHRvcDUkZ2VuZSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQoKI3dyaXRlLmNzdihDbHVzdGVyTWFya2VycywiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9HbGlhMlJHQ2x1c3Rlck1hcmtlcnNfbmV3LmNzdiIpCgpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4wLjEnCkNsdXN0ZXJNYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHNldS5xLCBvbmx5LnBvcyA9IFRSVUUpCgp0b3A1IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obj01LCB3dCA9IGF2Z19sb2cyRkMpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSB0b3A1JGdlbmUsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKCgpgYGAKTWFya2VycyBvZiAyIGFyZSBtYXRjaGluZyB3aXRoIDUgcG9zc2libHkgbWVyZ2UgdGhlc2UgdG9nZXRoZXIKQ2x1c3RlciAwIG1hcmtlcnMgZG9uJ3QgbG9vayB1cCByZWd1bGF0ZWQgYnV0IHRoZSBsaXN0IGlzIGxvbmcKCkxvb2sgYXQgdGhlIGxpYnJhcmllcwoKYGBge3J9CmxpYnJhcnkoZW5yaWNoUikKCnNldEVucmljaHJTaXRlKCJFbnJpY2hyIikgIyBIdW1hbiBnZW5lcwojIGxpc3Qgb2YgYWxsIHRoZSBkYXRhYmFzZXMKCiMgbGliYXJpZXMgd2l0aCBjZWxsIHR5cGVzCgpkYiA8LSBjKCdEZXNjYXJ0ZXNfQ2VsbF9UeXBlc19hbmRfVGlzc3VlXzIwMjEnLAogICAgICAgICdDZWxsTWFya2VyX0F1Z21lbnRlZF8yMDIxJywnQXppbXV0aF9DZWxsX1R5cGVzXzIwMjEnKQoKIyBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBOVUxMKQoKI0knbGwgcnVuIHRoZSBjbHVzdGVycyBvbmUgYXQgYSB0aW1lCgpOMS5jMCA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMCAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jMCRnZW5lCgpOMS5jMC5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jMC5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMC5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMC5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmMwLkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzAuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jMC5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgoKCgoKCmBgYAoKVGhlIGFkdWx0IGJyYWluIGRvZXNuJ3QgcHJlZGljdCByYWRpYWwgZ2xpYSAtIHRoZXJlIGFyZSByYWRpYWwgZ2xpYSBidXQgSSBiZWxpZXZlIHRoZXNlIGFyZSBkaWZmZXJlbnQgZnJvbSB0aGUgJ25ldXJvYmxhc3QnIHR5cGUgcmFkaWFsIGdsaWEKCkkgd2lsbCB1c2UgYSBkZXZlbG9waW5nIGJyYWluIHJlZmVyZW5jZQoKYGBge3J9CkRpbVBsb3Qoc2V1LnEpCgpgYGAKCgoKYGBge3J9CgpzZXUucSA8LSByZWFkUkRTKHBhc3RlKHBhdGh3YXksIkdsaWEyTGFibGVkU2V1MDMxMDIwMjIuUkRTIixzZXAgPSAiIikpCgojIG1pZGJyYWluIGFuZCBzdHJpYXR1bQoKc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9CaGFkdXJpX3dob2xlQnJhaW4vQmhhZHVyaV9taWRicmFpbl9zdHJpYXR1bS5SRFMiKQoKSWRlbnRzKHNldS5yKSA8LSAiY2VsbF9jbHVzdGVyIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1LnIsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gc2V1LnIkY2VsbF9jbHVzdGVyKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQgPC0gSWRlbnRzKHNldS5xKQpwcmludCh0YWJsZShzZXUucSRCaGEubWlkLnN0cmkucHJlZCkpCgoKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgIm5vbmUiKQpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5taWQucHJlZC50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEubWlkLnByZWQudGhyZXNoJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRCaGEubWlkLnByZWQudGhyZXNoKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEubWlkLnByZWQudGhyZXNoJykKCgojIHJlYWQgaW4gdGhlIHJlZmVyZW5jZSBkYXRhc2V0CiMgd2hvbGUgYnJhaW4gQmhhZHVyaSBkb3duIHNhbXBsZWQKc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9CaGFkdXJpX3dob2xlQnJhaW4vQmhhZHVyaV9kb3duc2FtcGxlLlJEUyIpCgpJZGVudHMoc2V1LnIpIDwtICJjZWxsX2NsdXN0ZXIiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRjZWxsX2NsdXN0ZXIpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRCaGEucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJEJoYS5wcmVkKSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQmhhLnByZWQnKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdzdWJncm91cHMnKQoKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgIm5vbmUiKQpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5taWQucHJlZC50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEucHJlZC50aHJlc2gnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJEJoYS5taWQucHJlZC50aHJlc2gpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5wcmVkLnRocmVzaCcpCgoKYGBgCgoKCgoKQWRkIHNvbWUgY2VsbCB0eXBlIGFubm90YXRpb25zCgpgYGB7cn0KCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuMicKCmNsdXN0ZXIuaWRzIDwtIGMoIkdsaWExIiwiUkcxIiwiR2xpYTIiLCJSRzIiLCJOZXVyb25zSW1tYXR1cmUiLCJSRzMiKQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUucSkKc2V1LnEgPC0gUmVuYW1lSWRlbnRzKHNldS5xLCBjbHVzdGVyLmlkcykKc2V1LnEkc3ViZ3JvdXBzIDwtIElkZW50cyhzZXUucSkKCiNEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjInLCBsYWJlbCA9IFRSVUUpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdzdWJncm91cHMnLCByZXBlbCA9IFRSVUUpCgoKCmBgYAoKYGBge3J9CiMgc2F2ZSBmaWxlCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTJMYWJsZWRTZXUwMzEwMjAyMi5SRFMiKQoKYGBgCgpNYWluIGNlbGwgZ3JvdXBzCgpgYGB7cn0KCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuMicKCmNsdXN0ZXIuaWRzIDwtIGMoIlJhZGlhbCBHbGlhIiwiUmFkaWFsIEdsaWEiLCJSYWRpYWwgR2xpYSIsIlJhZGlhbCBHbGlhIiwiTlBDIiwiT3RoZXIiKQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUucSkKc2V1LnEgPC0gUmVuYW1lSWRlbnRzKHNldS5xLCBjbHVzdGVyLmlkcykKc2V1LnEkQ2VsbF9UeXBlcyA8LSBJZGVudHMoc2V1LnEpCgojRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC4yJywgbGFiZWwgPSBUUlVFKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnQ2VsbF9UeXBlcycsIHJlcGVsID0gVFJVRSkKCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTJMYWJsZWRTZXUwMzEwMjAyMi5SRFMiKQoKYGBgCgpQcm9wb3J0aW9ucyBvZiBjZWxsIHR5cGVzCgpgYGB7cn0KCnRhYmxlKHNldS5xJENlbGxfVHlwZXMpCmRpbShzZXUucSkKCnBycCA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJENlbGxfVHlwZXMpKQpwcnAKCnBycCRwcm9wIDwtIHBycCRGcmVxL3N1bShwcnAkRnJlcSkqMTAwCnBycCRTYW1wbGUgPC0gJ1JhZGlhbEdsaWEnCnBycAoKCmBgYAoKSSdsbCBjYWxjdWxhdGUgdGhlIHByb3BvcnRpb25zIGZvciBlYWNoIGNlbGwgdHlwZSBhbmQgbWFrZSBhIHRhYmxlIG9yIHBsb3QgaW4gdGhlIGNvbXBhcmlzb24gd29ya2Jvb2suIAoKCgo=